From 504fe1fb5df280e7ba5909261c46a94388224982 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 29 Oct 2025 22:38:44 +0800 Subject: [PATCH 01/15] fix: update TOC item access in DocTemplate component - Modified the condition for accessing TOC items to ensure it handles undefined cases correctly. - Updated the dependency array in the useMemo hook to include the entire toc object for better stability in TOC data generation. --- src/templates/DocTemplate.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/templates/DocTemplate.tsx b/src/templates/DocTemplate.tsx index 19af47d3..36767104 100644 --- a/src/templates/DocTemplate.tsx +++ b/src/templates/DocTemplate.tsx @@ -145,7 +145,7 @@ function DocTemplate({ const pageType = getPageType(language, pageUrl); const rightTocData: TableOfContent[] | undefined = React.useMemo(() => { let tocItems: TableOfContent[] = []; - if (toc.items?.length === 1) { + if (toc?.items?.length === 1) { tocItems = toc.items![0].items || []; } else { tocItems = toc.items || []; @@ -153,7 +153,7 @@ function DocTemplate({ // Filter TOC based on CustomContent conditions return filterRightToc(tocItems, pageType, cloudPlan, language); - }, [toc.items, pageType, cloudPlan, language]); + }, [toc, pageType, cloudPlan, language]); const stableBranch = getStable(pathConfig.repo); From a468ab219c3d9847702ecf0ec191133f7c28ee51 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Mon, 10 Nov 2025 15:43:53 +0800 Subject: [PATCH 02/15] feat: add support for premium slug in generateConfig function (#647) * feat: add support for premium slug in generateConfig function - Introduced a new condition to handle slugs that include "premium/", allowing for better categorization in the configuration generation process. * refactor: update CloudPlan import paths across components - Replaced imports of CloudPlan from "shared/useCloudPlan" to "shared/interface" for consistency and clarity. - Adjusted related components to ensure proper usage of the updated CloudPlan type definition. * feat: extend EXTENDS_FOLDERS to include premium plan - Updated the EXTENDS_FOLDERS constant to include "premium" as a valid CloudPlan option, enhancing the flexibility of the table of contents generation. - Adjusted the type definition to ensure consistency with the new CloudPlan import. * feat: add CloudPlan type to path.ts for enhanced configuration - Imported CloudPlan into path.ts to support additional configuration options. - Updated the prefix variable type to CloudPlan | undefined for improved type safety in the generateConfig function. * feat: restructure page creation logic by modularizing create-pages functionality - Removed the existing create-pages.ts file and replaced it with modular files for creating specific pages: create-docs, create-doc-home, create-cloud-api, create-search, and create-404. - Introduced an interface file to define common types and constants, enhancing code organization and maintainability. - Each new file encapsulates the logic for creating its respective page, improving clarity and separation of concerns in the page generation process. * refactor: simplify prefix determination in generateConfig function - Replaced multiple conditional checks for slug prefixes with a more concise approach using an array and the find method. - Maintained the existing logic for handling the dedicated prefix based on the presence of a name, improving code readability and maintainability. * fix: update template import paths in page creation files - Adjusted the import paths for template files in create-404, create-cloud-api, create-doc-home, create-docs, and create-search to ensure correct resolution. - Enhanced consistency across page creation logic by standardizing the path structure. --- gatsby/cloud-plan.ts | 2 +- gatsby/create-pages.ts | 366 ------------------ gatsby/create-pages/create-404.ts | 22 ++ gatsby/create-pages/create-cloud-api.ts | 41 ++ gatsby/create-pages/create-doc-home.ts | 124 ++++++ gatsby/create-pages/create-docs.ts | 174 +++++++++ gatsby/create-pages/create-search.ts | 21 + gatsby/create-pages/index.ts | 5 + gatsby/create-pages/interface.ts | 13 + gatsby/path.ts | 20 +- gatsby/toc.ts | 14 +- .../MDXComponents/CustomContent.tsx | 2 +- src/components/MDXContent.tsx | 8 +- src/shared/filterRightToc.ts | 3 +- src/shared/interface.ts | 2 + src/shared/useCloudPlan.ts | 4 +- src/templates/DocTemplate.tsx | 2 +- 17 files changed, 435 insertions(+), 388 deletions(-) delete mode 100644 gatsby/create-pages.ts create mode 100644 gatsby/create-pages/create-404.ts create mode 100644 gatsby/create-pages/create-cloud-api.ts create mode 100644 gatsby/create-pages/create-doc-home.ts create mode 100644 gatsby/create-pages/create-docs.ts create mode 100644 gatsby/create-pages/create-search.ts create mode 100644 gatsby/create-pages/index.ts create mode 100644 gatsby/create-pages/interface.ts diff --git a/gatsby/cloud-plan.ts b/gatsby/cloud-plan.ts index 37032a26..fd2e9c49 100644 --- a/gatsby/cloud-plan.ts +++ b/gatsby/cloud-plan.ts @@ -1,7 +1,7 @@ import { mdxAstToToc, TocQueryData } from "./toc"; import { generateConfig } from "./path"; import { extractFilesFromToc } from "./toc-filter"; -import { CloudPlan } from "shared/useCloudPlan"; +import { CloudPlan } from "shared/interface"; type TocMap = Map< string, diff --git a/gatsby/create-pages.ts b/gatsby/create-pages.ts deleted file mode 100644 index a44c2921..00000000 --- a/gatsby/create-pages.ts +++ /dev/null @@ -1,366 +0,0 @@ -import { resolve } from "path"; - -import type { CreatePagesArgs } from "gatsby"; -import sig from "signale"; - -import { Locale, Repo, BuildType } from "../src/shared/interface"; -import { - generateConfig, - generateUrl, - generateNav, - generateDocHomeUrl, - generateStarterNav, - generateEssentialNav, -} from "./path"; -import { docs as DOCS_CONFIG } from "../docs/docs.json"; -import { cpMarkdown } from "./cp-markdown"; -import { - getTidbCloudFilesFromTocs, - determineInDefaultPlan, -} from "./cloud-plan"; -import { getFilesFromTocs, filterNodesByToc } from "./toc-filter"; - -interface PageQueryData { - allMdx: { - nodes: { - id: string; - frontmatter: { aliases: string[] }; - slug: string; - }[]; - }; -} - -const DEFAULT_BUILD_TYPE: BuildType = "prod"; - -export const createDocs = async (createPagesArgs: CreatePagesArgs) => { - const { - actions: { createPage, createRedirect }, - graphql, - } = createPagesArgs; - // const template = resolve(__dirname, '../src/doc/index.tsx') - const template = resolve(__dirname, "../src/templates/DocTemplate.tsx"); - - // First, get the list of files that should be built based on TOC content - const tocFilesMap = await getFilesFromTocs(graphql); - - // Get tidbcloud specific TOC files for plan determination - const tidbCloudTocFilesMap = await getTidbCloudFilesFromTocs(graphql); - - const docs = await graphql(` - { - allMdx( - filter: { - fileAbsolutePath: { regex: "/^(?!.*TOC).*$/" } - slug: { - nin: ["en/tidb/_docHome", "zh/tidb/_docHome", "ja/tidb/_docHome"] - } - frontmatter: { draft: { ne: true } } - } - ) { - nodes { - id - frontmatter { - aliases - } - slug - parent { - ... on File { - relativePath - } - } - } - } - } - `); - - if (docs.errors) { - sig.error(docs.errors); - } - - const nodes = filterNodesByToc( - docs.data!.allMdx.nodes.map((node) => { - const { config, name, filePath } = generateConfig(node.slug); - return { ...node, pathConfig: config, name, filePath }; - }), - tocFilesMap - ); - - sig.info( - `Building ${nodes.length} files after TOC filtering (from ${ - docs.data!.allMdx.nodes.length - } total files)` - ); - - // Log some statistics about the filtering - const filteredByToc = docs.data!.allMdx.nodes.length - nodes.length; - if (filteredByToc > 0) { - sig.info(`Filtered out ${filteredByToc} files not referenced in TOCs`); - } - - const versionRecord = nodes.reduce( - (acc, { pathConfig, name }) => { - if (acc[pathConfig.locale][pathConfig.repo] == null) { - acc[pathConfig.locale][pathConfig.repo] = {}; - } - - if (acc[pathConfig.locale][pathConfig.repo][name] == null) { - acc[pathConfig.locale][pathConfig.repo][name] = []; - } - - acc[pathConfig.locale][pathConfig.repo][name].push(pathConfig.version); - - return acc; - }, - { - en: {} as Record>, - zh: {} as Record>, - ja: {} as Record>, - } - ); - - nodes.forEach((node) => { - const { id, name, pathConfig, filePath } = node; - - if (name?.startsWith("_")) { - return; - } - - const path = generateUrl(name, pathConfig); - const navUrl = generateNav(pathConfig); - const starterNavUrl = generateStarterNav(pathConfig); - const essentialNavUrl = generateEssentialNav(pathConfig); - - const locale = [Locale.en, Locale.zh, Locale.ja] - .map((l) => - versionRecord[l][pathConfig.repo]?.[name]?.includes(pathConfig.version) - ? l - : undefined - ) - .filter(Boolean); - - // Determine inDefaultPlan for tidbcloud articles - const inDefaultPlan = determineInDefaultPlan( - name, - pathConfig, - tidbCloudTocFilesMap - ); - - cpMarkdown(`${node.slug}.md`, path, name); - createPage({ - path, - component: template, - context: { - id, - name, - pathConfig, - // use for edit in github - filePath, - pageUrl: path, - navUrl, - starterNavUrl, - essentialNavUrl, - availIn: { - locale, - version: versionRecord[pathConfig.locale][pathConfig.repo][name], - }, - buildType: (process.env.WEBSITE_BUILD_TYPE ?? - DEFAULT_BUILD_TYPE) as BuildType, // prod | archive, default is prod; archive is for archive site - feature: { - banner: true, - feedback: true, - }, - inDefaultPlan, - }, - }); - - // create redirects - if (node.frontmatter.aliases) { - node.frontmatter.aliases.forEach((fromPath: string) => { - createRedirect({ - fromPath, - toPath: path, - isPermanent: true, - }); - }); - } - }); -}; - -export const createCloudAPIReference = async ({ - actions: { createPage, createRedirect }, - graphql, -}: CreatePagesArgs) => { - const template = resolve( - __dirname, - "../src/templates/CloudAPIReferenceTemplate.tsx" - ); - const pageCfg = DOCS_CONFIG.tidbcloud.openAPI; - const pageList = pageCfg.data; - const locale = [Locale.en]; - - pageList.forEach((page) => { - const path = `/tidbcloud/${pageCfg.path}/${page.pathname}`; - const isProduction = process.env.CI === "true"; - createPage({ - path, - component: template, - context: { - ...page, - isProduction, - availIn: { - locale, - version: [], - }, - buildType: (process.env.WEBSITE_BUILD_TYPE ?? - DEFAULT_BUILD_TYPE) as BuildType, - feature: { - banner: false, - }, - }, - }); - }); -}; - -export const createDocHome = async ({ - actions: { createPage, createRedirect }, - graphql, -}: CreatePagesArgs) => { - // const template = resolve(__dirname, "../src/doc/index.tsx"); - const template = resolve(__dirname, "../src/templates/DocTemplate.tsx"); - - const prodQueryStr = ` - { - allMdx( - filter: { - fileAbsolutePath: { regex: "/tidb/master/_docHome.md$/" } - frontmatter: { draft: { ne: true } } - } - ) { - nodes { - id - frontmatter { - aliases - } - slug - parent { - ... on File { - relativePath - } - } - } - } - } -`; - - const archiveQueryStr = ` - { - allMdx( - filter: { - fileAbsolutePath: { regex: "/tidb/_docHome.md$/" } - frontmatter: { draft: { ne: true } } - } - ) { - nodes { - id - frontmatter { - aliases - } - slug - parent { - ... on File { - relativePath - } - } - } - } - } -`; - - const docs = await graphql( - process.env.WEBSITE_BUILD_TYPE === "archive" - ? archiveQueryStr - : prodQueryStr - ); - - if (docs.errors) { - sig.error(docs.errors); - } - - const nodes = docs.data!.allMdx.nodes.map((node) => { - const { config, name, filePath } = generateConfig(node.slug); - return { ...node, pathConfig: config, name, filePath }; - }); - - nodes.forEach((node) => { - const { id, name, pathConfig, filePath, slug } = node; - const path = generateDocHomeUrl(name, pathConfig); - const navUrl = generateNav(pathConfig); - const starterNavUrl = generateStarterNav(pathConfig); - const essentialNavUrl = generateEssentialNav(pathConfig); - const locale = - process.env.WEBSITE_BUILD_TYPE === "archive" - ? [Locale.en, Locale.zh] - : [Locale.en, Locale.zh, Locale.ja]; - - createPage({ - path, - component: template, - context: { - id, - name, - pathConfig, - // use for edit in github - filePath, - navUrl, - starterNavUrl, - essentialNavUrl, - pageUrl: path, - availIn: { - locale, - version: [], - }, - buildType: (process.env.WEBSITE_BUILD_TYPE ?? - DEFAULT_BUILD_TYPE) as BuildType, - feature: { - banner: true, - feedback: true, - globalHome: true, - }, - }, - }); - }); -}; - -export const createDocSearch = async ({ - actions: { createPage }, -}: CreatePagesArgs) => { - const template = resolve(__dirname, "../src/templates/DocSearchTemplate.tsx"); - - createPage({ - path: "/search/", - component: template, - context: { - feature: { - banner: false, - }, - }, - }); -}; - -export const create404 = async ({ - actions: { createPage }, -}: CreatePagesArgs) => { - const template = resolve(__dirname, "../src/templates/404Template.tsx"); - - createPage({ - path: "/404/", - component: template, - context: { - buildType: (process.env.WEBSITE_BUILD_TYPE ?? - DEFAULT_BUILD_TYPE) as BuildType, - feature: { - banner: false, - }, - }, - }); -}; diff --git a/gatsby/create-pages/create-404.ts b/gatsby/create-pages/create-404.ts new file mode 100644 index 00000000..14087517 --- /dev/null +++ b/gatsby/create-pages/create-404.ts @@ -0,0 +1,22 @@ +import { resolve } from "path"; +import type { CreatePagesArgs } from "gatsby"; +import { DEFAULT_BUILD_TYPE } from "./interface"; +import { BuildType } from "../../src/shared/interface"; + +export const create404 = async ({ + actions: { createPage }, +}: CreatePagesArgs) => { + const template = resolve(__dirname, "../../src/templates/404Template.tsx"); + + createPage({ + path: "/404/", + component: template, + context: { + buildType: (process.env.WEBSITE_BUILD_TYPE ?? + DEFAULT_BUILD_TYPE) as BuildType, + feature: { + banner: false, + }, + }, + }); +}; diff --git a/gatsby/create-pages/create-cloud-api.ts b/gatsby/create-pages/create-cloud-api.ts new file mode 100644 index 00000000..41d5f73f --- /dev/null +++ b/gatsby/create-pages/create-cloud-api.ts @@ -0,0 +1,41 @@ +import { resolve } from "path"; + +import type { CreatePagesArgs } from "gatsby"; + +import { Locale, BuildType } from "../../src/shared/interface"; +import { docs as DOCS_CONFIG } from "../../docs/docs.json"; +import { DEFAULT_BUILD_TYPE } from "./interface"; + +export const createCloudAPIReference = async ({ + actions: { createPage }, +}: CreatePagesArgs) => { + const template = resolve( + __dirname, + "../../src/templates/CloudAPIReferenceTemplate.tsx" + ); + const pageCfg = DOCS_CONFIG.tidbcloud.openAPI; + const pageList = pageCfg.data; + const locale = [Locale.en]; + + pageList.forEach((page) => { + const path = `/tidbcloud/${pageCfg.path}/${page.pathname}`; + const isProduction = process.env.CI === "true"; + createPage({ + path, + component: template, + context: { + ...page, + isProduction, + availIn: { + locale, + version: [], + }, + buildType: (process.env.WEBSITE_BUILD_TYPE ?? + DEFAULT_BUILD_TYPE) as BuildType, + feature: { + banner: false, + }, + }, + }); + }); +}; diff --git a/gatsby/create-pages/create-doc-home.ts b/gatsby/create-pages/create-doc-home.ts new file mode 100644 index 00000000..76f5618c --- /dev/null +++ b/gatsby/create-pages/create-doc-home.ts @@ -0,0 +1,124 @@ +import { resolve } from "path"; + +import type { CreatePagesArgs } from "gatsby"; +import sig from "signale"; + +import { Locale, BuildType } from "../../src/shared/interface"; +import { + generateConfig, + generateNav, + generateDocHomeUrl, + generateStarterNav, + generateEssentialNav, +} from "../../gatsby/path"; +import { DEFAULT_BUILD_TYPE, PageQueryData } from "./interface"; + +export const createDocHome = async ({ + actions: { createPage }, + graphql, +}: CreatePagesArgs) => { + // const template = resolve(__dirname, "../src/doc/index.tsx"); + const template = resolve(__dirname, "../../src/templates/DocTemplate.tsx"); + + const prodQueryStr = ` + { + allMdx( + filter: { + fileAbsolutePath: { regex: "/tidb/master/_docHome.md$/" } + frontmatter: { draft: { ne: true } } + } + ) { + nodes { + id + frontmatter { + aliases + } + slug + parent { + ... on File { + relativePath + } + } + } + } + } +`; + + const archiveQueryStr = ` + { + allMdx( + filter: { + fileAbsolutePath: { regex: "/tidb/_docHome.md$/" } + frontmatter: { draft: { ne: true } } + } + ) { + nodes { + id + frontmatter { + aliases + } + slug + parent { + ... on File { + relativePath + } + } + } + } + } +`; + + const docs = await graphql( + process.env.WEBSITE_BUILD_TYPE === "archive" + ? archiveQueryStr + : prodQueryStr + ); + + if (docs.errors) { + sig.error(docs.errors); + } + + const nodes = docs.data!.allMdx.nodes.map((node) => { + const { config, name, filePath } = generateConfig(node.slug); + return { ...node, pathConfig: config, name, filePath }; + }); + + nodes.forEach((node) => { + const { id, name, pathConfig, filePath, slug } = node; + const path = generateDocHomeUrl(name, pathConfig); + const navUrl = generateNav(pathConfig); + const starterNavUrl = generateStarterNav(pathConfig); + const essentialNavUrl = generateEssentialNav(pathConfig); + const locale = + process.env.WEBSITE_BUILD_TYPE === "archive" + ? [Locale.en, Locale.zh] + : [Locale.en, Locale.zh, Locale.ja]; + + createPage({ + path, + component: template, + context: { + id, + name, + pathConfig, + // use for edit in github + filePath, + navUrl, + starterNavUrl, + essentialNavUrl, + pageUrl: path, + availIn: { + locale, + version: [], + }, + buildType: (process.env.WEBSITE_BUILD_TYPE ?? + DEFAULT_BUILD_TYPE) as BuildType, + feature: { + banner: true, + feedback: true, + globalHome: true, + }, + }, + }); + }); +}; diff --git a/gatsby/create-pages/create-docs.ts b/gatsby/create-pages/create-docs.ts new file mode 100644 index 00000000..ca266fbf --- /dev/null +++ b/gatsby/create-pages/create-docs.ts @@ -0,0 +1,174 @@ +import { resolve } from "path"; + +import type { CreatePagesArgs } from "gatsby"; +import sig from "signale"; + +import { Locale, Repo, BuildType } from "../../src/shared/interface"; +import { + generateConfig, + generateUrl, + generateNav, + generateStarterNav, + generateEssentialNav, +} from "../../gatsby/path"; +import { cpMarkdown } from "../../gatsby/cp-markdown"; +import { + getTidbCloudFilesFromTocs, + determineInDefaultPlan, +} from "../cloud-plan"; +import { getFilesFromTocs, filterNodesByToc } from "../toc-filter"; +import { PageQueryData, DEFAULT_BUILD_TYPE } from "./interface"; + +export const createDocs = async (createPagesArgs: CreatePagesArgs) => { + const { + actions: { createPage, createRedirect }, + graphql, + } = createPagesArgs; + // const template = resolve(__dirname, '../src/doc/index.tsx') + const template = resolve(__dirname, "../../src/templates/DocTemplate.tsx"); + + // First, get the list of files that should be built based on TOC content + const tocFilesMap = await getFilesFromTocs(graphql); + + // Get tidbcloud specific TOC files for plan determination + const tidbCloudTocFilesMap = await getTidbCloudFilesFromTocs(graphql); + + const docs = await graphql(` + { + allMdx( + filter: { + fileAbsolutePath: { regex: "/^(?!.*TOC).*$/" } + slug: { + nin: ["en/tidb/_docHome", "zh/tidb/_docHome", "ja/tidb/_docHome"] + } + frontmatter: { draft: { ne: true } } + } + ) { + nodes { + id + frontmatter { + aliases + } + slug + parent { + ... on File { + relativePath + } + } + } + } + } + `); + + if (docs.errors) { + sig.error(docs.errors); + } + + const nodes = filterNodesByToc( + docs.data!.allMdx.nodes.map((node) => { + const { config, name, filePath } = generateConfig(node.slug); + return { ...node, pathConfig: config, name, filePath }; + }), + tocFilesMap + ); + + sig.info( + `Building ${nodes.length} files after TOC filtering (from ${ + docs.data!.allMdx.nodes.length + } total files)` + ); + + // Log some statistics about the filtering + const filteredByToc = docs.data!.allMdx.nodes.length - nodes.length; + if (filteredByToc > 0) { + sig.info(`Filtered out ${filteredByToc} files not referenced in TOCs`); + } + + const versionRecord = nodes.reduce( + (acc, { pathConfig, name }) => { + if (acc[pathConfig.locale][pathConfig.repo] == null) { + acc[pathConfig.locale][pathConfig.repo] = {}; + } + + if (acc[pathConfig.locale][pathConfig.repo][name] == null) { + acc[pathConfig.locale][pathConfig.repo][name] = []; + } + + acc[pathConfig.locale][pathConfig.repo][name].push(pathConfig.version); + + return acc; + }, + { + en: {} as Record>, + zh: {} as Record>, + ja: {} as Record>, + } + ); + + nodes.forEach((node) => { + const { id, name, pathConfig, filePath } = node; + + if (name?.startsWith("_")) { + return; + } + + const path = generateUrl(name, pathConfig); + const navUrl = generateNav(pathConfig); + const starterNavUrl = generateStarterNav(pathConfig); + const essentialNavUrl = generateEssentialNav(pathConfig); + + const locale = [Locale.en, Locale.zh, Locale.ja] + .map((l) => + versionRecord[l][pathConfig.repo]?.[name]?.includes(pathConfig.version) + ? l + : undefined + ) + .filter(Boolean); + + // Determine inDefaultPlan for tidbcloud articles + const inDefaultPlan = determineInDefaultPlan( + name, + pathConfig, + tidbCloudTocFilesMap + ); + + cpMarkdown(`${node.slug}.md`, path, name); + createPage({ + path, + component: template, + context: { + id, + name, + pathConfig, + // use for edit in github + filePath, + pageUrl: path, + navUrl, + starterNavUrl, + essentialNavUrl, + availIn: { + locale, + version: versionRecord[pathConfig.locale][pathConfig.repo][name], + }, + buildType: (process.env.WEBSITE_BUILD_TYPE ?? + DEFAULT_BUILD_TYPE) as BuildType, // prod | archive, default is prod; archive is for archive site + feature: { + banner: true, + feedback: true, + }, + inDefaultPlan, + }, + }); + + // create redirects + if (node.frontmatter.aliases) { + node.frontmatter.aliases.forEach((fromPath: string) => { + createRedirect({ + fromPath, + toPath: path, + isPermanent: true, + }); + }); + } + }); +}; diff --git a/gatsby/create-pages/create-search.ts b/gatsby/create-pages/create-search.ts new file mode 100644 index 00000000..3fd6c571 --- /dev/null +++ b/gatsby/create-pages/create-search.ts @@ -0,0 +1,21 @@ +import { resolve } from "path"; +import type { CreatePagesArgs } from "gatsby"; + +export const createDocSearch = async ({ + actions: { createPage }, +}: CreatePagesArgs) => { + const template = resolve( + __dirname, + "../../src/templates/DocSearchTemplate.tsx" + ); + + createPage({ + path: "/search/", + component: template, + context: { + feature: { + banner: false, + }, + }, + }); +}; diff --git a/gatsby/create-pages/index.ts b/gatsby/create-pages/index.ts new file mode 100644 index 00000000..2efeba8e --- /dev/null +++ b/gatsby/create-pages/index.ts @@ -0,0 +1,5 @@ +export { createDocHome } from "./create-doc-home"; +export { createDocs } from "./create-docs"; +export { createCloudAPIReference } from "./create-cloud-api"; +export { createDocSearch } from "./create-search"; +export { create404 } from "./create-404"; diff --git a/gatsby/create-pages/interface.ts b/gatsby/create-pages/interface.ts new file mode 100644 index 00000000..a87d2723 --- /dev/null +++ b/gatsby/create-pages/interface.ts @@ -0,0 +1,13 @@ +import { BuildType } from "../../src/shared/interface"; + +export const DEFAULT_BUILD_TYPE: BuildType = "prod"; + +export interface PageQueryData { + allMdx: { + nodes: { + id: string; + frontmatter: { aliases: string[] }; + slug: string; + }[]; + }; +} diff --git a/gatsby/path.ts b/gatsby/path.ts index 384c2254..44a223b0 100644 --- a/gatsby/path.ts +++ b/gatsby/path.ts @@ -1,4 +1,4 @@ -import { Locale, Repo, PathConfig } from "../src/shared/interface"; +import { Locale, Repo, PathConfig, CloudPlan } from "../src/shared/interface"; import CONFIG from "../docs/docs.json"; export function generateUrl(filename: string, config: PathConfig) { @@ -56,17 +56,17 @@ export function generateConfig(slug: string): { let name = rest[rest.length - 1]; name = name === "_index" ? "" : name; - let prefix = undefined; + let prefix: CloudPlan | undefined = undefined; if (repo === Repo.tidbcloud) { - if (slug.includes("starter/")) { - prefix = "starter"; - } else if (slug.includes("essential/")) { - prefix = "essential"; - } else if (slug.includes("dedicated/")) { - if (!!name) { - prefix = "dedicated"; - } + const simplePrefixes = ["starter", "essential", "premium"]; + prefix = simplePrefixes.find((p) => slug.includes(`${p}/`)) as + | CloudPlan + | undefined; + + // dedicated prefix is only used when the name is not empty + if (!prefix && slug.includes("dedicated/") && name) { + prefix = "dedicated"; } } diff --git a/gatsby/toc.ts b/gatsby/toc.ts index f037db07..bce566ca 100644 --- a/gatsby/toc.ts +++ b/gatsby/toc.ts @@ -9,10 +9,20 @@ import { Heading, } from "mdast"; -import { RepoNav, RepoNavLink, PathConfig } from "../src/shared/interface"; +import { + RepoNav, + RepoNavLink, + PathConfig, + CloudPlan, +} from "../src/shared/interface"; import { generateUrl } from "./path"; -export const EXTENDS_FOLDERS = ["starter", "essential", "dedicated"]; +export const EXTENDS_FOLDERS: CloudPlan[] = [ + "starter", + "essential", + "dedicated", + "premium", +]; const SKIP_MODE_HEADING = "_BUILD_ALLOWLIST"; diff --git a/src/components/MDXComponents/CustomContent.tsx b/src/components/MDXComponents/CustomContent.tsx index 9f2f1a3b..aefe6c8c 100644 --- a/src/components/MDXComponents/CustomContent.tsx +++ b/src/components/MDXComponents/CustomContent.tsx @@ -1,6 +1,6 @@ import { PropsWithChildren } from "react"; import { PageType } from "shared/utils"; -import { CloudPlan } from "shared/useCloudPlan"; +import { CloudPlan } from "shared/interface"; interface CustomContentProps { platform?: "tidb" | "tidb-cloud"; diff --git a/src/components/MDXContent.tsx b/src/components/MDXContent.tsx index 8b629b7e..615c03ba 100644 --- a/src/components/MDXContent.tsx +++ b/src/components/MDXContent.tsx @@ -8,13 +8,17 @@ import Box from "@mui/material/Box"; import * as MDXComponents from "components/MDXComponents"; import { CustomNotice } from "components/Card/CustomNotice"; -import { PathConfig, FrontMatter, BuildType } from "shared/interface"; +import { + PathConfig, + FrontMatter, + BuildType, + CloudPlan, +} from "shared/interface"; import { useTotalContributors } from "components/Contributors"; import replaceInternalHref from "shared/utils/anchor"; import { Pre } from "components/MDXComponents/Pre"; import { useCustomContent } from "components/MDXComponents/CustomContent"; import { getPageType } from "shared/utils"; -import { CloudPlan } from "shared/useCloudPlan"; export default function MDXContent(props: { data: any; diff --git a/src/shared/filterRightToc.ts b/src/shared/filterRightToc.ts index 59048afb..0b1e5754 100644 --- a/src/shared/filterRightToc.ts +++ b/src/shared/filterRightToc.ts @@ -1,6 +1,5 @@ -import { TableOfContent } from "./interface"; +import { TableOfContent, CloudPlan } from "./interface"; import { PageType } from "./utils"; -import { CloudPlan } from "./useCloudPlan"; /** * Filter right TOC based on CustomContent conditions diff --git a/src/shared/interface.ts b/src/shared/interface.ts index e7a8a314..97cd2b16 100644 --- a/src/shared/interface.ts +++ b/src/shared/interface.ts @@ -67,3 +67,5 @@ export interface RepoNavLink { export type RepoNav = RepoNavLink[]; export type BuildType = "prod" | "archive"; + +export type CloudPlan = "dedicated" | "starter" | "essential" | "premium"; diff --git a/src/shared/useCloudPlan.ts b/src/shared/useCloudPlan.ts index beac6547..34b0cdc4 100644 --- a/src/shared/useCloudPlan.ts +++ b/src/shared/useCloudPlan.ts @@ -7,15 +7,13 @@ import { useEffect, useState, } from "react"; -import { Repo } from "./interface"; +import { CloudPlan, Repo } from "./interface"; export const CLOUD_MODE_KEY = "plan"; export const CLOUD_MODE_VALUE_STARTER = "starter"; export const CLOUD_MODE_VALUE_ESSENTIAL = "essential"; export const CLOUD_MODE_VALUE_PREMIUM = "premium"; -export type CloudPlan = "dedicated" | "starter" | "essential" | "premium"; - const CloudPlanContext = createContext<{ repo: Repo; cloudPlan: CloudPlan | null; diff --git a/src/templates/DocTemplate.tsx b/src/templates/DocTemplate.tsx index 36767104..7fcfccaa 100644 --- a/src/templates/DocTemplate.tsx +++ b/src/templates/DocTemplate.tsx @@ -23,6 +23,7 @@ import { RepoNav, BuildType, Locale, + CloudPlan, } from "shared/interface"; import Seo from "components/Seo"; import { getStable, generateUrl, getPageType } from "shared/utils"; @@ -32,7 +33,6 @@ import { FeedbackSurveyCampaign } from "components/Campaign/FeedbackSurvey"; import { DOC_HOME_URL } from "shared/resources"; import { useReportReadingRate } from "shared/useReportReadingRate"; import { - CloudPlan, CloudPlanProvider, useCloudPlan, useCloudPlanNavigate, From fdcfd3095616c2ee9c51ebbdcca2988b01861208 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 19 Nov 2025 11:11:45 +0800 Subject: [PATCH 03/15] chore: add disk usage checks throughout production workflow - Introduced disk usage checks at various stages of the production workflow to monitor system resource usage. - Added checks after Docker image cleanup, node setup, dependency installation, cache restoration, and before/after deployment steps for better resource management. --- .github/workflows/production.yml | 37 ++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index b65f1f69..31f96b78 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -39,15 +39,21 @@ jobs: - name: Check disk usage run: df -h - name: Remove unused Docker images - run: docker system prune -af + run: | + docker system prune -af + df -h - uses: actions/checkout@v3 with: ref: "master" + - name: Check disk usage after checkout + run: df -h - uses: actions/setup-node@v4 with: node-version: 22 cache: "yarn" + - name: Check disk usage after setup-node + run: df -h - name: Update submodule id: submodule @@ -61,9 +67,12 @@ jobs: echo "submodule_status=$(git submodule status)" >> $GITHUB_OUTPUT echo "git_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "build_date=$(date)" >> $GITHUB_OUTPUT + df -h - name: Install deps - run: yarn + run: | + yarn + df -h - name: Calc gatsby cache key id: calc-cache-key @@ -74,6 +83,7 @@ jobs: else echo "key=${{ steps.submodule.outputs.submodule_sha }}" >> $GITHUB_OUTPUT fi + df -h - name: Restore gatsby cache uses: actions/cache@v3 @@ -85,16 +95,20 @@ jobs: key: ${{ runner.os }}-gatsby-cache-docs-staging-${{ steps.calc-cache-key.outputs.key }} restore-keys: | ${{ runner.os }}-gatsby-cache-docs-staging- + - name: Check disk usage after restore cache + run: df -h - name: Clean up temporary files run: | sudo rm -rf /tmp/* sudo rm -rf /home/runner/work/_temp/* + df -h - name: Clean cache if: ${{ inputs.nocache }} run: | yarn clean + df -h - name: Build website if: ${{ !inputs.fallback || (steps.gatsby-cache.outputs.cache-hit != 'true') }} @@ -108,18 +122,21 @@ jobs: GATSBY_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} run: | yarn build + df -h - name: Check output id: check-output run: | sudo apt install tree tree public + df -h - name: Install coscli run: | wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-linux-amd64 mv coscli-linux-amd64 coscli chmod 755 coscli + df -h - name: Deploy to COS if: ${{ !inputs.cos_backup }} @@ -134,6 +151,7 @@ jobs: --delete \ --exclude ".*\.(md|txt)$" \ --force + df -h - name: Deploy md to COS if: ${{ !inputs.cos_backup }} @@ -148,6 +166,7 @@ jobs: --include ".*\.(md|txt)$" \ --meta "Content-Type:text/plain; charset=utf-8" \ --force + df -h - name: Deploy to COS backup if: ${{ inputs.cos_backup }} @@ -162,6 +181,7 @@ jobs: --delete \ --exclude ".*\.(md|txt)$" \ --force + df -h - name: Deploy md to COS backup if: ${{ inputs.cos_backup }} @@ -176,6 +196,7 @@ jobs: --include ".*\.(md|txt)$" \ --meta "Content-Type:text/plain; charset=utf-8" \ --force + df -h cdn-refresh: needs: build @@ -187,15 +208,23 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Check disk usage after checkout + run: df -h - name: Set up Python environment uses: actions/setup-python@v5 with: python-version: "3.12" architecture: "x64" + - name: Check disk usage after setup-python + run: df -h - name: Install Tencent Cloud CLI - run: pipx install tccli + run: | + pipx install tccli + df -h - name: Purge production CDN cache - run: tccli cdn PurgePathCache --Paths '["https://docs.pingcap.com/"]' --FlushType flush + run: | + tccli cdn PurgePathCache --Paths '["https://docs.pingcap.com/"]' --FlushType flush + df -h From 720f4c20dd9257c78680ba1c605238b0e28eb432 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 19 Nov 2025 11:20:30 +0800 Subject: [PATCH 04/15] chore: enhance production workflow by maximizing build space - Added a step to maximize build space using the easimon/maximize-build-space action, allowing for better resource management during builds. - Included additional disk usage checks to monitor space before and after the build process, improving overall workflow efficiency. --- .github/workflows/production.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index b65f1f69..15622fa9 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -38,8 +38,16 @@ jobs: steps: - name: Check disk usage run: df -h - - name: Remove unused Docker images - run: docker system prune -af + + - name: Maximize build space + uses: easimon/maximize-build-space@master + with: + root-reserve-mb: 512 + swap-size-mb: 1024 + remove-dotnet: 'true' + + - name: Check disk usage + run: df -h - uses: actions/checkout@v3 with: @@ -65,6 +73,9 @@ jobs: - name: Install deps run: yarn + - name: Check disk usage + run: df -h + - name: Calc gatsby cache key id: calc-cache-key run: | From 20ccf3ef9aad3f6c5a0597e41413f24064770dd3 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 19 Nov 2025 11:23:07 +0800 Subject: [PATCH 05/15] refactor: streamline production workflow commands for clarity - Consolidated multiple run commands into single block executions for installing dependencies and purging CDN cache, enhancing readability and maintainability of the workflow script. --- .github/workflows/production.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 15622fa9..9f2e9223 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -71,10 +71,9 @@ jobs: echo "build_date=$(date)" >> $GITHUB_OUTPUT - name: Install deps - run: yarn - - - name: Check disk usage - run: df -h + run: | + yarn + df -h - name: Calc gatsby cache key id: calc-cache-key @@ -206,7 +205,9 @@ jobs: architecture: "x64" - name: Install Tencent Cloud CLI - run: pipx install tccli + run: | + pipx install tccli - name: Purge production CDN cache - run: tccli cdn PurgePathCache --Paths '["https://docs.pingcap.com/"]' --FlushType flush + run: | + tccli cdn PurgePathCache --Paths '["https://docs.pingcap.com/"]' --FlushType flush From 74062cd84cc9a4a5ea6f8354651cef4a17a69b14 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 19 Nov 2025 11:35:47 +0800 Subject: [PATCH 06/15] chore: rename markdown source in gatsby-config for consistency - Updated the name of the markdown source from `markdown-pages1` to `markdown-pages` for improved clarity and consistency in the configuration. --- gatsby-config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gatsby-config.js b/gatsby-config.js index e8fef110..a0d7fdae 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -104,9 +104,8 @@ module.exports = { { resolve: `gatsby-source-filesystem`, options: { - name: `markdown-pages1`, + name: `markdown-pages`, path: `${__dirname}/docs/markdown-pages`, - ignore: ["**/ja/**", "**/zh/**"], }, }, { From a33d630ec6add3684252ffed1f0d97489dcafd18 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Wed, 19 Nov 2025 11:36:35 +0800 Subject: [PATCH 07/15] refactor: optimize disk space management in production workflow - Removed redundant disk usage checks and consolidated cleanup commands into a single run step to streamline the production workflow. - Enhanced disk space management by directly removing unnecessary directories and pruning Docker images, improving build efficiency. --- .github/workflows/production.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 9f2e9223..7af5ba27 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -36,18 +36,16 @@ jobs: runs-on: ubuntu-latest name: Build website steps: - - name: Check disk usage - run: df -h - - name: Maximize build space - uses: easimon/maximize-build-space@master - with: - root-reserve-mb: 512 - swap-size-mb: 1024 - remove-dotnet: 'true' - - - name: Check disk usage - run: df -h + run: | + df -h + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + sudo docker builder prune -a + df -h - uses: actions/checkout@v3 with: From 9c6bc1f05e8e0518f1d8fb608416a44945d916e2 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Fri, 21 Nov 2025 15:28:29 +0800 Subject: [PATCH 08/15] Update sentry sample rate (#650) * chore: adjust tracesSampleRate in Sentry configuration for improved performance * fix: improve node removal safety in useTotalContributors hook * fix: correct dependency array in TabContentDetector to ensure proper rendering behavior * fix: enhance TabContentDetector to prevent duplicate rendering of tabs - Added renderedTabs prop to SimpleTab and TabContentDetector components. - Updated useEffect in TabContentDetector to check if the tab has already been rendered, ensuring proper rendering behavior and preventing duplicate calls to onRendered. * feat: update HeaderNav to conditionally render cloud plan links - Integrated useCloudPlan hook to retrieve the current cloud plan. - Modified navigation links in HeaderNavStack and HeaderNavStackMobile to dynamically adjust the URL based on the cloud plan, enhancing user experience. * fix: correct optional chaining in filterRightToc function - Removed optional chaining for item.items to ensure proper filtering of nested items in the filterRightToc function, enhancing functionality. --- sentry.config.js | 2 +- src/components/Contributors/index.tsx | 8 +++++++- src/components/Layout/HeaderNav.tsx | 16 +++++++++++++--- src/components/Layout/Navigation/RightNav.tsx | 2 +- src/components/MDXComponents/SimpleTab.tsx | 11 +++++++++-- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/sentry.config.js b/sentry.config.js index f37c53e0..993b487b 100644 --- a/sentry.config.js +++ b/sentry.config.js @@ -5,5 +5,5 @@ Sentry.init({ dsn: process.env.GATSBY_SENTRY_DSN, integrations: [Sentry.browserTracingIntegration()], - tracesSampleRate: 0.1, + tracesSampleRate: 0.01, }); diff --git a/src/components/Contributors/index.tsx b/src/components/Contributors/index.tsx index 8d275ccb..1a9a360f 100644 --- a/src/components/Contributors/index.tsx +++ b/src/components/Contributors/index.tsx @@ -237,7 +237,13 @@ export const useTotalContributors = ( return; } ReactDOM.unmountComponentAtNode(appendedNode); - appendedNode?.parentNode?.removeChild(appendedNode); + // Check if the node is still a child of its parent before removing + if ( + appendedNode.parentNode && + appendedNode.parentNode.contains(appendedNode) + ) { + appendedNode.parentNode.removeChild(appendedNode); + } }; }, [totalContributors]); diff --git a/src/components/Layout/HeaderNav.tsx b/src/components/Layout/HeaderNav.tsx index 4477f564..fc1406f4 100644 --- a/src/components/Layout/HeaderNav.tsx +++ b/src/components/Layout/HeaderNav.tsx @@ -23,6 +23,7 @@ import { BuildType } from "shared/interface"; import { GTMEvent, gtmTrack } from "shared/utils/gtm"; import TiDBLogo from "media/logo/tidb-logo-withtext.svg"; +import { CLOUD_MODE_KEY, useCloudPlan } from "shared/useCloudPlan"; // `pageUrl` comes from server side render (or build): gatsby/path.ts/generateUrl // it will be `undefined` in client side render @@ -46,6 +47,7 @@ export default function HeaderNavStack(props: { }) { const { language, t } = useI18next(); const selectedItem = useSelectedNavItem(language, props.pageUrl); + const { cloudPlan } = useCloudPlan(); return ( )} @@ -161,7 +167,7 @@ export function HeaderNavStackMobile(props: { buildType?: BuildType }) { const theme = useTheme(); const { language, t } = useI18next(); const selectedItem = useSelectedNavItem(language); - + const { cloudPlan } = useCloudPlan(); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -240,7 +246,11 @@ export function HeaderNavStackMobile(props: { buildType?: BuildType }) { > gtmTrack(GTMEvent.ClickHeadNav, { diff --git a/src/components/Layout/Navigation/RightNav.tsx b/src/components/Layout/Navigation/RightNav.tsx index d81142d4..91d8294a 100644 --- a/src/components/Layout/Navigation/RightNav.tsx +++ b/src/components/Layout/Navigation/RightNav.tsx @@ -83,7 +83,7 @@ export default function RightNav(props: RightNavProps) { headingIds.push(id); } } - if (item.items) { + if (item?.items) { collectIds(item.items); } }); diff --git a/src/components/MDXComponents/SimpleTab.tsx b/src/components/MDXComponents/SimpleTab.tsx index 5f18a9c6..e133f7a6 100644 --- a/src/components/MDXComponents/SimpleTab.tsx +++ b/src/components/MDXComponents/SimpleTab.tsx @@ -104,6 +104,7 @@ export function SimpleTab({ key={id} id={id} activeTab={activeTab} + renderedTabs={renderedTabs} onRendered={() => { setRenderedTabs((prev) => { if (prev.includes(id)) { @@ -135,19 +136,25 @@ export const TabContentDetector = ({ children, id, activeTab, + renderedTabs, onRendered, }: { children: ReactElement; id: string; activeTab: string; + renderedTabs: string[]; onRendered: (id: string) => void; }) => { const ref = useRef(null); useEffect(() => { - if (ref.current && ref.current.children.length > 0) { + if ( + ref.current && + ref.current.children.length > 0 && + !renderedTabs.includes(id) + ) { onRendered(id); } - }, []); + }); return (
{children} From 27c48f23c772fe47104e0790d65c046b50275568 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Sat, 22 Nov 2025 17:44:32 +0800 Subject: [PATCH 09/15] fix: update HeaderNav links to handle undefined cloud plans - Modified the navigation links in HeaderNavStack and HeaderNavStackMobile to include a condition that allows for rendering the default URL when the cloud plan is undefined, improving user experience and navigation reliability. --- src/components/Layout/HeaderNav.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Layout/HeaderNav.tsx b/src/components/Layout/HeaderNav.tsx index fc1406f4..d3950301 100644 --- a/src/components/Layout/HeaderNav.tsx +++ b/src/components/Layout/HeaderNav.tsx @@ -67,7 +67,7 @@ export default function HeaderNavStack(props: { selected={selectedItem === "tidbcloud"} label={t("navbar.cloud")} to={ - cloudPlan === "dedicated" + cloudPlan === "dedicated" || !cloudPlan ? `/tidbcloud` : `/tidbcloud/${cloudPlan}?${CLOUD_MODE_KEY}=${cloudPlan}` } @@ -247,7 +247,7 @@ export function HeaderNavStackMobile(props: { buildType?: BuildType }) { Date: Mon, 24 Nov 2025 16:27:28 +0800 Subject: [PATCH 10/15] src, static: update cn.pingcap.com links (#652) --- src/shared/resources.ts | 24 ++++++++++++------------ static/zh/llms.txt | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/shared/resources.ts b/src/shared/resources.ts index 54e630b9..04229be9 100644 --- a/src/shared/resources.ts +++ b/src/shared/resources.ts @@ -17,27 +17,27 @@ export const DOC_HOME_URL = "https://docs.pingcap.com"; export const DEFAULT_PINGCAP_URL = `https://www.pingcap.com`; export const EN_PINGCAP_URL = `https://www.pingcap.com/`; -export const ZH_PINGCAP_URL = `https://cn.pingcap.com/`; +export const ZH_PINGCAP_URL = `https://pingkai.cn/`; export const JA_PINGCAP_URL = `https://pingcap.co.jp/`; export const EN_PINGCAP_DOWNLOAD_URL = "https://www.pingcap.com/download/"; export const ZH_PINGCAP_DOWNLOAD_URL = - "https://cn.pingcap.com/product/#SelectProduct"; + "https://pingkai.cn/opensource/tidb"; export const JA_PINGCAP_DOWNLOAD_URL = "https://pingcap.co.jp/event/"; export const EN_CONTACT_URL = "https://www.pingcap.com/contact-us/"; -export const ZH_CONTACT_URL = "https://cn.pingcap.com/contact/"; +export const ZH_CONTACT_URL = "https://pingkai.cn/contact"; export const JA_CONTACT_URL = "https://pingcap.co.jp/contact-us/"; export const EN_PRIVACY_POLICY_URL = "https://www.pingcap.com/privacy-policy"; -export const ZH_PRIVACY_POLICY_URL = "https://cn.pingcap.com/privacy-policy"; +export const ZH_PRIVACY_POLICY_URL = "https://pingkai.cn/legal/privacy-policy"; export const JA_PRIVACY_POLICY_URL = "https://pingcap.co.jp/privacy-policy"; export const EN_LEARNING_CENTER_URL = "https://www.pingcap.com/education/"; -export const ZH_LEARNING_CENTER_URL = "https://cn.pingcap.com/education/"; +export const ZH_LEARNING_CENTER_URL = "https://learn.pingcap.cn/learner/course"; export const EN_LEGAL_URL = "https://www.pingcap.com/legal/"; -export const ZH_LEGAL_URL = "https://cn.pingcap.com/law/"; +export const ZH_LEGAL_URL = "https://pingkai.cn/legal/compliance"; export const JA_LEGAL_URL = "https://pingcap.co.jp/legal/"; export const ICON_GROUP_CHUNK_SIZE = 3; @@ -305,7 +305,7 @@ export const ZH_FOOTER_ITEMS = [ }, { name: "TiDB", - url: "https://cn.pingcap.com/product/#SelectProduct", + url: "https://pingkai.cn/opensource/tidb", }, { name: "TiDB Cloud", @@ -347,11 +347,11 @@ export const ZH_FOOTER_ITEMS = [ }, { name: "博客", - url: "https://cn.pingcap.com/blog/", + url: "https://tidb.net/blog", }, { name: "Education", - url: "https://cn.pingcap.com/education/", + url: "https://learn.pingcap.cn/learner/course", }, ], }, @@ -364,7 +364,7 @@ export const ZH_FOOTER_ITEMS = [ }, { name: "联系我们", - url: "https://cn.pingcap.com/contact/", + url: "https://pingkai.cn/contact", }, ], }, @@ -373,7 +373,7 @@ export const ZH_FOOTER_ITEMS = [ items: [ { name: "关于我们", - url: "https://cn.pingcap.com/about-us?tab=companyOverview", + url: "https://pingkai.cn/about", }, { name: "招贤纳士", @@ -381,7 +381,7 @@ export const ZH_FOOTER_ITEMS = [ }, { name: "新闻报道", - url: "https://cn.pingcap.com/company-activity/", + url: "https://pingkai.cn/news", }, ], }, diff --git a/static/zh/llms.txt b/static/zh/llms.txt index 22ea2628..44f69c25 100644 --- a/static/zh/llms.txt +++ b/static/zh/llms.txt @@ -5,7 +5,7 @@ ## TiDB Self-Managed 文档(使用 TiUP 部署) - [TiDB Self-Managed 文档](https://docs.pingcap.com/zh/tidb/stable.md): TiDB 是 PingCAP 公司自主设计、研发的开源分布式关系型数据库。产品文档包括了 TiDB 简介、功能概览、TiFlash、快速上手 TiDB、HTAP、开发者手册概览、软硬件环境需求、使用 TiUP 部署 TiDB、数据迁移概览、运维、监控、调优、工具、TiDB 路线图、配置文件参数、命令行参数、TiDB Control、系统变量、发布历史、常见问题。 -- [TiDB 版本周期支持策略](https://cn.pingcap.com/tidb-release-support-policy/): 阐述了 PingCAP 就 TiDB 版本提供支持服务的标准和规则。 +- [TiDB 版本周期支持策略](https://pingkai.cn/tidb-release-support-policy): 阐述了 PingCAP 就 TiDB 版本提供支持服务的标准和规则。 - [50 TiB 数据导入最佳实践](https://docs.pingcap.com/zh/tidb/stable/data-import-best-practices.md): 了解将大规模数据导入 TiDB 的最佳实践。 - [ADD COLUMN](https://docs.pingcap.com/zh/tidb/stable/sql-statement-add-column.md): TiDB 数据库中 ADD COLUMN 的使用概况。 - [ADD INDEX](https://docs.pingcap.com/zh/tidb/stable/sql-statement-add-index.md): TiDB 数据库中 ADD INDEX 的使用概况。 From c35718b1558584e79c91198eaca2c26749109b29 Mon Sep 17 00:00:00 2001 From: WD Date: Tue, 25 Nov 2025 10:52:54 +0800 Subject: [PATCH 11/15] tweak: remove pingcap account track script (#654) --- gatsby-browser.js | 8 -------- scripts/track.js | 49 ----------------------------------------------- 2 files changed, 57 deletions(-) delete mode 100644 scripts/track.js diff --git a/gatsby-browser.js b/gatsby-browser.js index 72e84087..2be1b7e5 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -1,7 +1,5 @@ import "./src/styles/global.css"; -import { pageView } from "./scripts/track"; - export { default as wrapRootElement } from "./src/state/wrap-with-provider"; export const onClientEntry = () => { @@ -19,9 +17,3 @@ export const onClientEntry = () => { ===================================== `); }; - -export const onRouteUpdate = ({ location, prevLocation }) => { - if (process.env.NODE_ENV === "production") { - pageView(); - } -}; diff --git a/scripts/track.js b/scripts/track.js deleted file mode 100644 index f4a8d314..00000000 --- a/scripts/track.js +++ /dev/null @@ -1,49 +0,0 @@ -exports.pageView = function () { - _reportLog('pageview', { - dl: document.location.href, - dr: document.referrer, - ts: _getCurrentTS(), - ul: navigator.language || navigator.userLanguage, - dt: document.title, - }) -} - -function _getCurrentTS() { - return new Date().getTime() -} - -function _getCookie(cname) { - var name = cname + '=' - var ca = document.cookie.split(';') - for (var i = 0; i < ca.length; i++) { - var c = ca[i].trim() - if (c.indexOf(name) === 0) return c.substring(name.length, c.length) - } - return '' -} - -function _reportLog(logType, params) { - var eventUrl = 'https://accounts.pingcap.com/_analytics/event?' - var uid = _getCookie('uid') - - if (uid) { - params.uid = uid - } - - params.e = logType - // version - params.v = 1 - - var paramsArr = [] - Object.keys(params).forEach(item => { - if (typeof params[item] !== 'undefined') { - paramsArr.push(item + '=' + encodeURIComponent(params[item])) - } - }) - eventUrl += paramsArr.join('&') - - var oReq = new XMLHttpRequest() - oReq.open('GET', eventUrl) - oReq.withCredentials = true - oReq.send() -} From 91cad601d8cea6a1699ef166ebf348d733bbb893 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Tue, 25 Nov 2025 10:53:15 +0800 Subject: [PATCH 12/15] Adjust TOC to support the same article linking from multiple locations (#653) * feat: enhance navigation state management with session storage - Updated the LeftNavTree component to support saving and retrieving navigation item IDs from session storage, improving user experience by maintaining state across sessions. - Modified the calcExpandedIds function to accept an optional targetId for more precise matching. - Implemented helper functions to handle session storage interactions for navigation items. * refactor: simplify navigation state management in LeftNavTree component - Removed session storage interactions for expanded and selected navigation items, streamlining state management. - Updated the calcExpandedIds function calls to eliminate unnecessary parameters, enhancing clarity and performance. * refactor: simplify scroll behavior in scrollToElementIfInView function - Updated the scrollToElementIfInView function to directly call scrollIntoView with a "center" block option, removing unnecessary viewport checks and improving code clarity. --- .../Layout/Navigation/LeftNavTree.tsx | 49 +++++++++++++++++-- src/shared/utils/index.ts | 10 +--- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/components/Layout/Navigation/LeftNavTree.tsx b/src/components/Layout/Navigation/LeftNavTree.tsx index c31c02d6..4f033ae8 100644 --- a/src/components/Layout/Navigation/LeftNavTree.tsx +++ b/src/components/Layout/Navigation/LeftNavTree.tsx @@ -72,11 +72,18 @@ function StyledTreeItem(props: StyledTreeItemProps) { ); } -const calcExpandedIds = (data: RepoNavLink[], targetLink: string) => { +const calcExpandedIds = ( + data: RepoNavLink[], + targetLink: string, + targetId?: string +) => { const ids: string[] = []; const treeForeach = (data: RepoNavLink[], parents: string[] = []): void => { data.forEach((item) => { - if (item.link === targetLink) { + const isMatch = targetId + ? item.link === targetLink && item.id === targetId + : item.link === targetLink; + if (isMatch) { ids.push(...parents); ids.push(item.id); return; @@ -90,6 +97,29 @@ const calcExpandedIds = (data: RepoNavLink[], targetLink: string) => { return ids; }; +// Session storage key prefix for nav item id +const NAV_ITEM_ID_STORAGE_KEY = "nav_item_id_"; + +// Get nav item id from session storage for a given path +const getNavItemIdFromStorage = (path: string): string | null => { + if (typeof window === "undefined") return null; + try { + return sessionStorage.getItem(`${NAV_ITEM_ID_STORAGE_KEY}${path}`); + } catch { + return null; + } +}; + +// Save nav item id to session storage for a given path +const saveNavItemIdToStorage = (path: string, id: string): void => { + if (typeof window === "undefined") return; + try { + sessionStorage.setItem(`${NAV_ITEM_ID_STORAGE_KEY}${path}`, id); + } catch { + // Ignore storage errors + } +}; + export default function ControlledTreeView(props: { data: RepoNav; current: string; @@ -110,7 +140,12 @@ export default function ControlledTreeView(props: { const theme = useTheme(); React.useEffect(() => { - const expandedIds = calcExpandedIds(data, currentUrl); + const storedId = getNavItemIdFromStorage(currentUrl); + const expandedIds = calcExpandedIds( + data, + currentUrl, + storedId || undefined + ); setExpanded(expandedIds); expandedIds.length && setSelected([expandedIds[expandedIds.length - 1]]); }, [data, currentUrl]); @@ -191,7 +226,13 @@ export default function ControlledTreeView(props: { key={item.id} to={item.link} style={{ width: "100%", color: "inherit" }} - onClick={(e) => e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + // Save nav item id to session storage when clicked + if (item.link) { + saveNavItemIdToStorage(item.link, item.id); + } + }} > void } ) { - const isVisiable = isInViewport(element); - if (isVisiable) { - return; - } - if (element.scrollIntoViewIfNeeded) { - element.scrollIntoViewIfNeeded(); - } else { - element.scrollIntoView({ block: "end" }); - } + element.scrollIntoView({ block: "center" }); } export type PageType = "home" | "tidb" | "tidbcloud" | undefined; From 801c2f586cd5ba903fe0277920670670ab1b9d21 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Tue, 25 Nov 2025 11:35:17 +0800 Subject: [PATCH 13/15] refactor: reorganize imports and streamline createDocs function - Updated import paths for clarity and consistency. - Removed unused navigation URL generation functions to simplify the createDocs logic. - Introduced a default value for inDefaultPlan to enhance code readability. --- gatsby/create-pages/create-docs.ts | 46 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/gatsby/create-pages/create-docs.ts b/gatsby/create-pages/create-docs.ts index ca266fbf..81cb06b3 100644 --- a/gatsby/create-pages/create-docs.ts +++ b/gatsby/create-pages/create-docs.ts @@ -4,20 +4,22 @@ import type { CreatePagesArgs } from "gatsby"; import sig from "signale"; import { Locale, Repo, BuildType } from "../../src/shared/interface"; -import { - generateConfig, - generateUrl, - generateNav, - generateStarterNav, - generateEssentialNav, -} from "../../gatsby/path"; -import { cpMarkdown } from "../../gatsby/cp-markdown"; -import { - getTidbCloudFilesFromTocs, - determineInDefaultPlan, -} from "../cloud-plan"; +import { generateConfig, generateUrl, generateNav } from "../path"; +import { cpMarkdown } from "../cp-markdown"; +import { getTidbCloudFilesFromTocs } from "../cloud-plan"; import { getFilesFromTocs, filterNodesByToc } from "../toc-filter"; -import { PageQueryData, DEFAULT_BUILD_TYPE } from "./interface"; + +interface PageQueryData { + allMdx: { + nodes: { + id: string; + frontmatter: { aliases: string[] }; + slug: string; + }[]; + }; +} + +const DEFAULT_BUILD_TYPE: BuildType = "prod"; export const createDocs = async (createPagesArgs: CreatePagesArgs) => { const { @@ -25,7 +27,7 @@ export const createDocs = async (createPagesArgs: CreatePagesArgs) => { graphql, } = createPagesArgs; // const template = resolve(__dirname, '../src/doc/index.tsx') - const template = resolve(__dirname, "../../src/templates/DocTemplate.tsx"); + const template = resolve(__dirname, "../src/templates/DocTemplate.tsx"); // First, get the list of files that should be built based on TOC content const tocFilesMap = await getFilesFromTocs(graphql); @@ -114,8 +116,6 @@ export const createDocs = async (createPagesArgs: CreatePagesArgs) => { const path = generateUrl(name, pathConfig); const navUrl = generateNav(pathConfig); - const starterNavUrl = generateStarterNav(pathConfig); - const essentialNavUrl = generateEssentialNav(pathConfig); const locale = [Locale.en, Locale.zh, Locale.ja] .map((l) => @@ -126,11 +126,11 @@ export const createDocs = async (createPagesArgs: CreatePagesArgs) => { .filter(Boolean); // Determine inDefaultPlan for tidbcloud articles - const inDefaultPlan = determineInDefaultPlan( - name, - pathConfig, - tidbCloudTocFilesMap - ); + // const inDefaultPlan = determineInDefaultPlan( + // name, + // pathConfig, + // tidbCloudTocFilesMap + // ); cpMarkdown(`${node.slug}.md`, path, name); createPage({ @@ -144,8 +144,6 @@ export const createDocs = async (createPagesArgs: CreatePagesArgs) => { filePath, pageUrl: path, navUrl, - starterNavUrl, - essentialNavUrl, availIn: { locale, version: versionRecord[pathConfig.locale][pathConfig.repo][name], @@ -156,7 +154,7 @@ export const createDocs = async (createPagesArgs: CreatePagesArgs) => { banner: true, feedback: true, }, - inDefaultPlan, + inDefaultPlan: "premium", }, }); From 2b0c641de5e7baa2ff4c504bcc3dc59eddac15c9 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Tue, 25 Nov 2025 11:50:59 +0800 Subject: [PATCH 14/15] refactor: simplify HeaderNav links by removing cloud plan conditions - Removed cloud plan checks from navigation links in HeaderNavStack and HeaderNavStackMobile, streamlining the URL generation for the TiDB Cloud link. - Cleaned up unused imports related to cloud plan management for improved code clarity. --- src/components/Layout/HeaderNav.tsx | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/components/Layout/HeaderNav.tsx b/src/components/Layout/HeaderNav.tsx index 14f4104d..0e34a3e7 100644 --- a/src/components/Layout/HeaderNav.tsx +++ b/src/components/Layout/HeaderNav.tsx @@ -15,7 +15,6 @@ import { generateDownloadURL, generateContactURL, generateLearningCenterURL, - generateDocsHomeUrl, getPageType, PageType, } from "shared/utils"; @@ -23,7 +22,6 @@ import { BuildType } from "shared/interface"; import { GTMEvent, gtmTrack } from "shared/utils/gtm"; import TiDBLogo from "media/logo/tidb-logo-withtext.svg"; -import { CLOUD_MODE_KEY, useCloudPlan } from "shared/useCloudPlan"; // `pageUrl` comes from server side render (or build): gatsby/path.ts/generateUrl // it will be `undefined` in client side render @@ -47,7 +45,6 @@ export default function HeaderNavStack(props: { }) { const { language, t } = useI18next(); const selectedItem = useSelectedNavItem(language, props.pageUrl); - const { cloudPlan } = useCloudPlan(); return ( )} @@ -167,7 +160,6 @@ export function HeaderNavStackMobile(props: { buildType?: BuildType }) { const theme = useTheme(); const { language, t } = useI18next(); const selectedItem = useSelectedNavItem(language); - const { cloudPlan } = useCloudPlan(); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -246,11 +238,7 @@ export function HeaderNavStackMobile(props: { buildType?: BuildType }) { > gtmTrack(GTMEvent.ClickHeadNav, { From 7875e4d4a5d9386dee450dc3b12441b0240d0117 Mon Sep 17 00:00:00 2001 From: Suhaha Date: Tue, 25 Nov 2025 11:54:21 +0800 Subject: [PATCH 15/15] refactor: update createDocHome to target new documentation path - Changed the regex filter in createDocHome to point to the updated documentation file path for TiDB Cloud. - Removed unused navigation URL generation functions to streamline the code and improve clarity. --- gatsby/create-pages/create-doc-home.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gatsby/create-pages/create-doc-home.ts b/gatsby/create-pages/create-doc-home.ts index 76f5618c..0f2e1d01 100644 --- a/gatsby/create-pages/create-doc-home.ts +++ b/gatsby/create-pages/create-doc-home.ts @@ -8,8 +8,6 @@ import { generateConfig, generateNav, generateDocHomeUrl, - generateStarterNav, - generateEssentialNav, } from "../../gatsby/path"; import { DEFAULT_BUILD_TYPE, PageQueryData } from "./interface"; @@ -24,7 +22,7 @@ export const createDocHome = async ({ { allMdx( filter: { - fileAbsolutePath: { regex: "/tidb/master/_docHome.md$/" } + fileAbsolutePath: { regex: "/tidbcloud/master/_docHome.md$/" } frontmatter: { draft: { ne: true } } } ) { @@ -87,8 +85,6 @@ export const createDocHome = async ({ const { id, name, pathConfig, filePath, slug } = node; const path = generateDocHomeUrl(name, pathConfig); const navUrl = generateNav(pathConfig); - const starterNavUrl = generateStarterNav(pathConfig); - const essentialNavUrl = generateEssentialNav(pathConfig); const locale = process.env.WEBSITE_BUILD_TYPE === "archive" ? [Locale.en, Locale.zh] @@ -104,8 +100,6 @@ export const createDocHome = async ({ // use for edit in github filePath, navUrl, - starterNavUrl, - essentialNavUrl, pageUrl: path, availIn: { locale,