This is an automated email from the ASF dual-hosted git repository.

jeffreyh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git


The following commit(s) were added to refs/heads/master by this push:
     new cec304bd920 Generate v4 pdf (#3125)
cec304bd920 is described below

commit cec304bd920f96fb8b11154fdcec7b69c121acb6
Author: yangon <[email protected]>
AuthorDate: Fri Nov 28 16:57:37 2025 +0800

    Generate v4 pdf (#3125)
---
 scripts/renameFilesAndDirs.js    |  98 +++++++++++++++++++++++++++++++++++++++
 src/constant/download.data.ts    |   7 ++-
 src/constant/version.ts          |   4 +-
 src/theme/TOC/index.tsx          |   4 +-
 static/pdf/Apache_Doris_v4_x.pdf | Bin 0 -> 60602860 bytes
 5 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/scripts/renameFilesAndDirs.js b/scripts/renameFilesAndDirs.js
new file mode 100644
index 00000000000..8e17555ad0e
--- /dev/null
+++ b/scripts/renameFilesAndDirs.js
@@ -0,0 +1,98 @@
+const fs = require('fs');
+const path = require('path');
+
+/**
+ * Replace underscores with hyphens in filenames or directory names.
+ * @param {string} inputPath - relative paths to be processed
+ */
+async function renameFilesAndDirs(inputPath) {
+    const absolutePath = path.resolve(process.cwd(), inputPath);
+    
+    if (!fs.existsSync(absolutePath)) {
+        console.error(`❌ Path does not exist: ${absolutePath}`);
+        return;
+    }
+
+    console.log(`🔍 Start processing the path: ${absolutePath}`);
+    
+    try {
+        // Process subdirectories and files first, then process the current 
directory (depth-first).
+        await processDirectory(absolutePath);
+        console.log('✅ All files and directories have been renamed!');
+    } catch (error) {
+        console.error('❌ error:', error);
+    }
+}
+
+/**
+ * Recursive processing directory
+ */
+async function processDirectory(dirPath) {
+    const items = fs.readdirSync(dirPath);
+    
+    for (const item of items) {
+        const fullPath = path.join(dirPath, item);
+        const stats = fs.statSync(fullPath);
+        
+        if (stats.isDirectory()) {
+            await processDirectory(fullPath);
+            
+            await renameItem(fullPath);
+        } else if (stats.isFile() && isMarkdownFile(item)) {
+            await renameItem(fullPath);
+        }
+    }
+    
+    if (dirPath !== path.resolve(process.cwd(), inputPath)) {
+        await renameItem(dirPath);
+    }
+}
+
+/**
+ * Rename a file or directory
+ */
+async function renameItem(oldPath) {
+    const dirName = path.dirname(oldPath);
+    const oldName = path.basename(oldPath);
+    
+    if (!oldName.includes('_')) {
+        return;
+    }
+    
+    const newName = oldName.replace(/_/g, '-');
+    
+    if (oldName === newName) {
+        return;
+    }
+    
+    const newPath = path.join(dirName, newName);
+    
+    try {
+        if (fs.existsSync(newPath)) {
+            console.log(`⚠️  Skip, target already exists: ${newPath}`);
+            return;
+        }
+        
+        fs.renameSync(oldPath, newPath);
+        console.log(`✅ Rename: ${oldName} → ${newName}`);
+        
+    } catch (error) {
+        console.error(`❌ Rename failed: ${oldPath} → ${newPath}`, 
error.message);
+    }
+}
+
+/**
+ * Check if it is a Markdown file
+ */
+function isMarkdownFile(filename) {
+    return /\.(md|mdx)$/i.test(filename);
+}
+
+const inputPath = process.argv[2];
+
+if (!inputPath) {
+    console.error('❌ Please provide a valid path as an argument.');
+    process.exit(1);
+}
+
+renameFilesAndDirs(inputPath);
\ No newline at end of file
diff --git a/src/constant/download.data.ts b/src/constant/download.data.ts
index 38ca9e4c9a3..7d75acdaea5 100644
--- a/src/constant/download.data.ts
+++ b/src/constant/download.data.ts
@@ -2575,8 +2575,13 @@ export const RUN_ANYWHERE = [
 ];
 
 export const DOWNLOAD_PDFS = [
+     {
+        version: '4.x',
+        filename: 'Apache Doris 中文手册(v4.x).pdf',
+        link: 'https://doris.apache.org/pdf/Apache_Doris_v4_x.pdf',
+    },
     {
-        version: '3.0',
+        version: '3.x',
         filename: 'Apache Doris 中文手册(v3.0).pdf',
         link: 'https://doris.apache.org/pdf/Apache_Doris_v3_0_4412376f6e.pdf',
     },
diff --git a/src/constant/version.ts b/src/constant/version.ts
index 048d40f29a3..de901a1f413 100644
--- a/src/constant/version.ts
+++ b/src/constant/version.ts
@@ -1,2 +1,2 @@
-export const VERSIONS = ['1.2', '2.0', '2.1', '3.x', 'dev'];
-export const DEFAULT_VERSION = '3.x';
\ No newline at end of file
+export const VERSIONS = ['1.2', '2.0', '2.1', '3.x', '4.x', 'dev'];
+export const DEFAULT_VERSION = '3.x';
diff --git a/src/theme/TOC/index.tsx b/src/theme/TOC/index.tsx
index 130fa59df45..ad1a38a5041 100644
--- a/src/theme/TOC/index.tsx
+++ b/src/theme/TOC/index.tsx
@@ -55,7 +55,7 @@ export default function TOC({ className, ...props }: Props): 
React.ReactElement
             }
         }
     }, [typeof window !== 'undefined' && location.pathname]);
-
+    
     return (
         <div className={clsx(styles.tableOfContents, 'thin-scrollbar', 
'toc-container', className)}>
             <div style={isBrowser && location.pathname.startsWith('/blog') ? { 
display: 'none' } : {}}>
@@ -65,7 +65,7 @@ export default function TOC({ className, ...props }: Props): 
React.ReactElement
                         <span className="group-hover:text-[#444FD9]">{isCN ? 
'Doris 首页' : 'Doris Homepage'}</span>
                     </div>
                 </Link>
-                {isCN && ['3.x', '2.0', '2.1'].includes(currentVersion) ? (
+                {isCN && ['4.x','3.x', '2.0', '2.1'].includes(currentVersion) 
? (
                     <div
                         className="toc-icon-content group"
                         onClick={() => {
diff --git a/static/pdf/Apache_Doris_v4_x.pdf b/static/pdf/Apache_Doris_v4_x.pdf
new file mode 100644
index 00000000000..3a6434dfd8e
Binary files /dev/null and b/static/pdf/Apache_Doris_v4_x.pdf differ


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to