dochub 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # DocHub
2
+
3
+ DocHub is a tool for dynamically generating and displaying documentation from Markdown files converted to HTML. It includes real-time updates, search functionality, syntax highlighting, and dark mode support for better readability.
4
+
5
+ ## Features
6
+
7
+ - **Real-time Updates:** Automatically refreshes content when documentation files are modified.
8
+ - **Search Functionality:** Includes a search bar to filter through documentation content.
9
+ - **Syntax Highlighting:** Uses Highlight.js to format code blocks for better readability.
10
+ - **Dark Mode:** Implements a dark mode theme for improved viewing experience.
11
+
12
+ ## Getting Started
13
+
14
+ To start using DocHub, follow these steps:
15
+
16
+ 1. **Installation:**
17
+ - Ensure Node.js is installed on your system.
18
+
19
+ 2. **Initialize Project:**
20
+ - Run `dochub init` in your terminal to set up a new DocHub project.
21
+ - This command will create the necessary folder structure and configuration files.
22
+
23
+ 3. **Generate Documentation:**
24
+ - Once initialized, run `dochub serve [directory]` in your terminal.
25
+ - Replace `[directory]` with the path to your documentation files.
26
+
27
+ 4. **View Documentation:**
28
+ - Open a web browser and navigate to `http://localhost:3000`.
29
+ - The sidebar lists different sections of your documentation.
30
+ - Use the search bar to find specific topics within the documentation.
31
+
32
+ ## Project Structure
33
+
34
+ The project structure includes:
35
+
36
+ - **HTML and CSS:** Main structure and styling managed using HTML and Tailwind CSS.
37
+ - **JavaScript (Client-side):** Handles dynamic content updates, event handling, and integration with external libraries like Socket.IO and Highlight.js.
38
+ - **Socket.IO:** Facilitates real-time communication to update documentation on file changes.
39
+ - **Markdown to HTML Conversion:** Converts Markdown content fetched from the server-side into HTML for rendering.
40
+
41
+ ## Getting Help
42
+
43
+ If you encounter any issues or have questions about using DocHub, feel free to reach out via [GitHub Issues](https://github.com/your-repo/issues).
44
+
45
+ ## License
46
+
47
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
package/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ const yargs = require('yargs');
4
+ const initProject = require('./src/init');
5
+ const serveDocs = require('./src/serve');
6
+
7
+ yargs
8
+ .command({
9
+ command: 'init <directory>',
10
+ describe: 'Initialize a new documentation project',
11
+ builder: (yargs) => {
12
+ yargs.positional('directory', {
13
+ describe: 'Directory name for the documentation project',
14
+ type: 'string',
15
+ });
16
+ },
17
+ handler: initProject,
18
+ })
19
+ .command({
20
+ command: 'serve <directory>',
21
+ describe: 'Serve the documentation site',
22
+ builder: (yargs) => {
23
+ yargs.positional('directory', {
24
+ describe: 'Directory name where documentation files are located',
25
+ type: 'string',
26
+ });
27
+ },
28
+ handler: serveDocs,
29
+ })
30
+ .demandCommand()
31
+ .help()
32
+ .argv;
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "dochub",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "bin": {
9
+ "dochub": "./index.js"
10
+ },
11
+ "author": "Tyler Reece",
12
+ "license": "ISC",
13
+ "description": "DocHub is a documentation generation CLI tool.",
14
+ "dependencies": {
15
+ "child_process": "^1.0.2",
16
+ "fs": "^0.0.1-security",
17
+ "path": "^0.12.7",
18
+ "yargs": "^17.7.2"
19
+ }
20
+ }
package/src/init.js ADDED
@@ -0,0 +1,219 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function initProject(argv) {
6
+ const directory = argv.directory;
7
+
8
+ if (!directory) {
9
+ console.error('Please provide a directory name.');
10
+ return;
11
+ }
12
+
13
+ const projectPath = path.resolve(directory);
14
+
15
+ if (fs.existsSync(projectPath)) {
16
+ console.error(`Directory '${directory}' already exists.`);
17
+ return;
18
+ }
19
+
20
+ // Create the main documentation folder
21
+ fs.mkdirSync(projectPath);
22
+ animatedLog(`✔ Created directory '${projectPath}'.`, 50);
23
+
24
+ // Simulate folder creation with loading animation
25
+ setTimeout(() => {
26
+ const markdownFolder = path.join(projectPath, 'markdown');
27
+ fs.mkdirSync(markdownFolder, { recursive: true });
28
+ animatedLog(`✔ Created directory '${markdownFolder}'.`, 50);
29
+
30
+ // Create sample markdown files
31
+ createMarkdownFiles(markdownFolder);
32
+
33
+ // Create config.json
34
+ const configFilePath = path.join(projectPath, 'config.json');
35
+ createConfigFile(configFilePath);
36
+ animatedLog(`✔ Created file '${configFilePath}'.`, 50);
37
+
38
+ // Create index.html
39
+ const indexFilePath = path.join(projectPath, 'index.html');
40
+ createIndexFile(indexFilePath);
41
+ animatedLog(`✔ Created file '${indexFilePath}'.`, 50);
42
+
43
+ // Create server.js
44
+ const serverFilePath = path.join(projectPath, 'server.js');
45
+ createServerFile(serverFilePath, projectPath);
46
+ animatedLog(`✔ Created file '${serverFilePath}'.`, 50);
47
+
48
+ // Initialize npm and install necessary packages
49
+ initializeNpm(projectPath);
50
+ installPackages(projectPath);
51
+
52
+ console.log(`✔ Initialized new DocHub project in '${projectPath}'.`);
53
+ }, 1000); // Simulate some delay for a fun loading effect
54
+ }
55
+
56
+ // Function to create sample markdown files by copying from templates directory
57
+ function createMarkdownFiles(folderPath) {
58
+ const templatesDir = path.join(__dirname, '..', 'templates');
59
+
60
+ const markdownFiles = [
61
+ {
62
+ name: 'README.md',
63
+ templatePath: path.join(templatesDir, 'README.md')
64
+ },
65
+ {
66
+ name: 'guide.md',
67
+ templatePath: path.join(templatesDir, 'guide.md')
68
+ },
69
+ {
70
+ name: 'folder/README.md',
71
+ templatePath: path.join(templatesDir, 'folder', 'README.md')
72
+ }
73
+ ];
74
+
75
+ markdownFiles.forEach(file => {
76
+ const filePath = path.join(folderPath, file.name);
77
+
78
+ try {
79
+ // Ensure directory exists before writing file
80
+ const dirname = path.dirname(filePath);
81
+ if (!fs.existsSync(dirname)) {
82
+ fs.mkdirSync(dirname, { recursive: true });
83
+ }
84
+
85
+ const templateContent = fs.readFileSync(file.templatePath, 'utf8');
86
+ fs.writeFileSync(filePath, templateContent);
87
+ console.log(`✔ Created file: ${filePath}`);
88
+ } catch (err) {
89
+ console.error(`✘ Error creating file ${filePath}:`, err);
90
+ }
91
+ });
92
+ }
93
+
94
+
95
+ // Function to create config.json
96
+ function createConfigFile(filePath) {
97
+ const configData = {
98
+ title: 'Documentation Project',
99
+ description: 'Sample documentation project configuration'
100
+ // Add more configuration as needed
101
+ };
102
+
103
+ fs.writeFileSync(filePath, JSON.stringify(configData, null, 2));
104
+ animatedLog(`✔ Created file '${filePath}'.`, 50);
105
+ }
106
+
107
+ // Function to create index.html
108
+ function createIndexFile(filePath) {
109
+ const htmlContent = `
110
+ <!DOCTYPE html>
111
+ <html lang="en">
112
+ <head>
113
+ <meta charset="UTF-8">
114
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
115
+ <title>Documentation Project</title>
116
+ </head>
117
+ <body>
118
+ <h1>Welcome to Documentation Project</h1>
119
+ <p>This is the main index.html file for your documentation project.</p>
120
+ </body>
121
+ </html>
122
+ `;
123
+
124
+ fs.writeFileSync(filePath, htmlContent.trim());
125
+ animatedLog(`✔ Created file '${filePath}'.`, 50);
126
+ }
127
+
128
+ // Function to create server.js using a template
129
+ function createServerFile(filePath) {
130
+ const templatePath = path.join(__dirname, '..', 'templates', 'server.js');
131
+
132
+ // Read the server.js template file
133
+ fs.readFile(templatePath, 'utf8', (err, data) => {
134
+ if (err) {
135
+ console.error(`✘ Error reading template file: ${err}`);
136
+ return;
137
+ }
138
+
139
+ // Write the template content to the specified filePath
140
+ fs.writeFileSync(filePath, data.trim());
141
+ console.log(`✔ Server file generated at ${filePath}`);
142
+ });
143
+ }
144
+
145
+ // Function to initialize npm in the project directory
146
+ function initializeNpm(projectPath) {
147
+ const packageJson = {
148
+ name: path.basename(projectPath),
149
+ version: '1.0.0',
150
+ description: 'DocHub project',
151
+ scripts: {
152
+ start: 'node server.js'
153
+ },
154
+ keywords: [],
155
+ author: '',
156
+ license: 'ISC'
157
+ };
158
+
159
+ const packageJsonPath = path.join(projectPath, 'package.json');
160
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
161
+
162
+ try {
163
+ execSync('npm init -y', { cwd: projectPath, stdio: 'ignore' });
164
+ animatedLog('✔ Initialized npm.', 50);
165
+ } catch (error) {
166
+ console.error('✘ Failed to initialize npm.');
167
+ }
168
+ }
169
+
170
+ // Function to install necessary packages
171
+ function installPackages(projectPath) {
172
+ const dependencies = ['http', 'fs', 'path', 'highlight.js', 'socket.io']; // Add more dependencies as needed
173
+
174
+ animatedLog(`✔ Installing packages: ${dependencies.join(', ')}`, 50);
175
+ const progressBar = createProgressBar(20);
176
+
177
+ try {
178
+ execSync(`npm install --save ${dependencies.join(' ')}`, { cwd: projectPath, stdio: 'inherit' });
179
+ progressBar.stop();
180
+ console.log('\n✔ Installed necessary packages.');
181
+ } catch (error) {
182
+ progressBar.stop();
183
+ console.error('\n✘ Failed to install necessary packages.');
184
+ }
185
+ }
186
+
187
+ // Function to create a progress bar
188
+ function createProgressBar(totalFrames) {
189
+ let currentFrame = 0;
190
+ const progressBar = setInterval(() => {
191
+ process.stdout.write(`\r${frames[currentFrame]} Installing...`);
192
+ currentFrame = (currentFrame + 1) % totalFrames;
193
+ }, 50);
194
+
195
+ return {
196
+ stop: () => {
197
+ clearInterval(progressBar);
198
+ process.stdout.write(`\r✔ Installation complete.\n`);
199
+ }
200
+ };
201
+ }
202
+
203
+ // Function to animate console log with delay
204
+ function animatedLog(message, delay) {
205
+ const frames = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
206
+ let frame = 0;
207
+
208
+ const interval = setInterval(() => {
209
+ process.stdout.write(`\r${frames[frame]} ${message}`);
210
+ frame = (frame + 1) % frames.length;
211
+ }, delay);
212
+
213
+ setTimeout(() => {
214
+ clearInterval(interval);
215
+ process.stdout.write(`\r${message}\n`);
216
+ }, delay * 20); // Simulate loading completion
217
+ }
218
+
219
+ module.exports = initProject;
package/src/serve.js ADDED
@@ -0,0 +1,66 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ function serveDocs(argv) {
6
+ const directory = argv.directory || './docs';
7
+
8
+ const serverFilePath = path.resolve(directory, 'server.js');
9
+
10
+ if (!serverExists(serverFilePath)) {
11
+ console.error(`Server file '${serverFilePath}' not found. Make sure to run 'init' first.`);
12
+ return;
13
+ }
14
+
15
+ // Start the server using Node.js child process
16
+ const serverProcess = spawn('node', [serverFilePath]);
17
+
18
+ // Simulate server starting with loading animation
19
+ animatedLog(`Starting server from '${serverFilePath}'`, 50);
20
+
21
+ serverProcess.stdout.on('data', (data) => {
22
+ // Log server stdout with tick emoji
23
+ logWithTick(data.toString().trim());
24
+ });
25
+
26
+ serverProcess.stderr.on('data', (data) => {
27
+ // Log server stderr with tick emoji for errors
28
+ logWithTick(`Error: ${data.toString().trim()}`);
29
+ });
30
+
31
+ serverProcess.on('close', (code) => {
32
+ console.log(`Server process exited with code ${code}`);
33
+ });
34
+ }
35
+
36
+ // Function to check if server.js file exists
37
+ function serverExists(filePath) {
38
+ try {
39
+ return fs.existsSync(filePath);
40
+ } catch (err) {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ // Function to animate console log with delay
46
+ function animatedLog(message, delay) {
47
+ const frames = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
48
+ let frame = 0;
49
+
50
+ const interval = setInterval(() => {
51
+ process.stdout.write(`\r${frames[frame]} ${message}`);
52
+ frame = (frame + 1) % frames.length;
53
+ }, delay);
54
+
55
+ setTimeout(() => {
56
+ clearInterval(interval);
57
+ process.stdout.write(`\r✔ ${message}\n`);
58
+ }, delay * 20); // Simulate loading completion
59
+ }
60
+
61
+ // Function to log with tick emoji
62
+ function logWithTick(message) {
63
+ console.log(`✔ ${message}`);
64
+ }
65
+
66
+ module.exports = serveDocs;
@@ -0,0 +1,43 @@
1
+ # Welcome to your first documentation!
2
+
3
+ This project dynamically generates and displays documentation from Markdown files converted to HTML. It utilizes Socket.IO for real-time updates, integrates with Tailwind CSS for styling, and uses Highlight.js for code syntax highlighting.
4
+
5
+ ## Features
6
+
7
+ - **Real-time Updates:** Automatically refreshes content when documentation files are modified.
8
+ - **Search Functionality:** Includes a search bar to filter through documentation content.
9
+ - **Syntax Highlighting:** Uses Highlight.js to format code blocks for better readability.
10
+ - **Dark Mode:** Implements a dark mode theme for improved viewing experience.
11
+
12
+ ## Getting Started
13
+
14
+ To generate and view your documentation, follow these steps:
15
+
16
+ 1. **Installation:**
17
+ - Ensure Node.js is installed on your system.
18
+
19
+ 2. **Generate Documentation:**
20
+ - Run `docify serve [directory]` in your terminal.
21
+ - Replace `[directory]` with the path to your documentation files.
22
+
23
+ 3. **View Documentation:**
24
+ - Open a web browser and navigate to `http://localhost:3000`.
25
+ - The sidebar lists different sections of your documentation.
26
+ - Use the search bar to find specific topics within the documentation.
27
+
28
+ ## Project Structure
29
+
30
+ The project structure includes:
31
+
32
+ - **HTML and CSS:** Main structure and styling managed using HTML and Tailwind CSS.
33
+ - **JavaScript (Client-side):** Handles dynamic content updates, event handling, and integration with external libraries like Socket.IO and Highlight.js.
34
+ - **Socket.IO:** Facilitates real-time communication to update documentation on file changes.
35
+ - **Markdown to HTML Conversion:** Converts Markdown content fetched from the server-side into HTML for rendering.
36
+
37
+ ## Getting Help
38
+
39
+ If you encounter any issues or have questions about using this documentation project, feel free to reach out via [GitHub Issues](https://github.com/your-repo/issues).
40
+
41
+ ## License
42
+
43
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
@@ -0,0 +1,3 @@
1
+ # Readme
2
+
3
+ This is a readme in a folder.
@@ -0,0 +1,3 @@
1
+ # Guide
2
+
3
+ This is a guide.
@@ -0,0 +1,267 @@
1
+ const http = require('http');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const hljs = require('highlight.js');
5
+ const socketio = require('socket.io');
6
+
7
+ let htmlContent = ''; // Variable to store HTML content
8
+
9
+ // Function to convert Markdown to HTML
10
+ function convertMarkdownToHtml(markdownContent, filePath) {
11
+ // Regular expression to match Markdown code blocks
12
+ const codeBlockRegex = /```(\w+)\n([\s\S]*?)```/g;
13
+
14
+ // Basic conversion: Replace Markdown syntax with HTML equivalents
15
+ let htmlContent = markdownContent
16
+ // Code blocks
17
+ .replace(codeBlockRegex, (match, language, code) => {
18
+ const validLanguage = language || 'plaintext'; // Default to plaintext if no language specified
19
+ const highlightedCode = hljs.highlight(code, { language: validLanguage }).value;
20
+ // Count the number of lines in the code content
21
+ const lines = code.split('\n').length;
22
+ return `<pre class="bg-gray-800 text-white p-4 rounded-md mb-4 code-block" style="height: ${lines * 1.5}rem;"><code class="language-${validLanguage}" data-lines="${lines}">${highlightedCode}</code></pre>`;
23
+ })
24
+ // Inline code
25
+ .replace(/`([^`]*)`/g, '<code class="bg-gray-800 text-white rounded px-1">$1</code>')
26
+ // Headers, emphasis, lists, links
27
+ .replace(/^# (.*)$/gm, '<h1 class="text-4xl font-bold mt-8 mb-4">$1</h1>')
28
+ .replace(/^## (.*)$/gm, '<h2 class="text-3xl font-bold mt-6 mb-3">$1</h2>')
29
+ .replace(/^### (.*)$/gm, '<h3 class="text-2xl font-bold mt-4 mb-2">$1</h3>')
30
+ .replace(/^#### (.*)$/gm, '<h4 class="text-xl font-bold mt-3 mb-2">$1</h4>')
31
+ .replace(/^##### (.*)$/gm, '<h5 class="text-lg font-bold mt-3 mb-2">$1</h5>')
32
+ .replace(/^###### (.*)$/gm, '<h6 class="text-base font-bold mt-2 mb-2">$1</h6>')
33
+ .replace(/\*\*(.*)\*\*/g, '<strong class="font-bold">$1</strong>')
34
+ .replace(/\*(.*)\*/g, '<em class="italic">$1</em>')
35
+ .replace(/^\s*-\s(.*)$/gm, '<li class="list-disc ml-4">$1</li>')
36
+ .replace(/^\s*\d\.\s(.*)$/gm, '<li class="list-decimal ml-4">$1</li>')
37
+ .replace(/<li>(.*)<\/li>/g, '<ul class="list-inside list-disc mb-4">$1</ul>')
38
+ .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" class="text-blue-500 hover:underline">$1</a>')
39
+ // Paragraphs and Line breaks
40
+ .replace(/\n\n/gm, '</p><p class="mb-6">') // Replace double newline with closing and opening paragraph tags
41
+ .replace(/^\s*$/gm, '<p class="mb-6">') // Replace empty lines with opening paragraph tag
42
+ .replace(/^/gm, '') // Remove the beginning of each line
43
+ .replace(/$/gm, ''); // Remove the end of each line
44
+
45
+ // Generate sidebar based on file structure
46
+ const sidebar = generateSidebar(path.join(__dirname, 'markdown'));
47
+
48
+ // Return the entire HTML structure
49
+ return `
50
+ <!DOCTYPE html>
51
+ <html lang="en">
52
+ <head>
53
+ <meta charset="UTF-8">
54
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
55
+ <title>Markdown to HTML</title>
56
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
57
+ <link href="https://cdn.jsdelivr.net/npm/highlight.js/styles/vs2015.css" rel="stylesheet">
58
+ <style>
59
+ /* Dark mode theme */
60
+ body {
61
+ background-color: #1a202c;
62
+ color: #e2e8f0;
63
+ }
64
+ .bg-gray-200 {
65
+ background-color: #2d3748;
66
+ }
67
+ .text-blue-500 {
68
+ color: #90cdf4;
69
+ }
70
+ .hover\:underline:hover {
71
+ text-decoration: underline;
72
+ }
73
+ /* Adjust sidebar height */
74
+ .sidebar {
75
+ min-height: 100vh; /* Ensure sidebar stretches to full viewport height */
76
+ display: flex;
77
+ flex-direction: column;
78
+ align-items: flex-start; /* Align items to the start (left) */
79
+ justify-content: flex-start; /* Start content from the top */
80
+ padding: 20px;
81
+ }
82
+ .code-block {
83
+ color: #e2e8f0; /* Match the body text color */
84
+ padding: 1rem;
85
+ margin-bottom: 1rem;
86
+ border-radius: 0.5rem;
87
+ overflow: hidden; /* Prevents scroll bars */
88
+ white-space: pre-wrap; /* Preserves white space */
89
+ }
90
+ .search-bar {
91
+ width: 100%;
92
+ padding: 0.5rem;
93
+ margin-bottom: 1rem;
94
+ background-color: #4a5568;
95
+ color: #e2e8f0;
96
+ border: none;
97
+ border-radius: 0.25rem;
98
+ outline: none;
99
+ }
100
+ .search-bar:focus {
101
+ background-color: #2d3748;
102
+ }
103
+ </style>
104
+ </head>
105
+ <body class="flex bg-gray-100">
106
+ <aside class="w-1/5 bg-gray-800 p-4 sidebar">
107
+ <h1 class="text-xl font-bold mb-4">Sidebar</h1>
108
+ <input type="text" id="search-input" class="search-bar" placeholder="Search...">
109
+ <ul id="sidebar-list" class="space-y-2">
110
+ ${sidebar}
111
+ </ul>
112
+ </aside>
113
+ <main id="main-content" class="w-4/5 p-4 bg-gray-200">
114
+ ${htmlContent}
115
+ </main>
116
+ <script src="https://cdn.socket.io/4.3.1/socket.io.min.js"></script>
117
+ <script>
118
+ document.addEventListener('DOMContentLoaded', function() {
119
+ const socket = io(); // Declare socket variable here
120
+
121
+ socket.on('fileChange', function() {
122
+ fetchCurrentPageContent();
123
+ });
124
+
125
+ const codeBlocks = document.querySelectorAll('.code-block');
126
+ codeBlocks.forEach(block => {
127
+ const code = block.querySelector('code');
128
+ const lines = parseInt(code.getAttribute('data-lines'));
129
+ const lineHeight = parseInt(getComputedStyle(code).lineHeight);
130
+ block.style.height = (lines * lineHeight) + 'px'; // Set height using proper concatenation
131
+ });
132
+
133
+ // Fetch the sidebar list and handle search input
134
+ const sidebarList = document.getElementById('sidebar-list');
135
+ if (sidebarList) {
136
+ const sidebarItems = sidebarList.getElementsByTagName('li');
137
+
138
+ document.getElementById('search-input').addEventListener('input', function() {
139
+ const searchValue = this.value.trim().toLowerCase();
140
+
141
+ Array.from(sidebarItems).forEach(item => {
142
+ const link = item.querySelector('a');
143
+ if (link) {
144
+ const textContent = link.textContent.trim().toLowerCase();
145
+
146
+ if (textContent.includes(searchValue)) {
147
+ item.style.display = 'block';
148
+ } else {
149
+ item.style.display = 'none';
150
+ }
151
+ }
152
+ });
153
+ });
154
+ } else {
155
+ console.error('Sidebar list element not found.');
156
+ }
157
+ });
158
+
159
+ function fetchCurrentPageContent() {
160
+ // Fetch the current page's HTML content
161
+ fetch(window.location.href)
162
+ .then(response => response.text())
163
+ .then(html => {
164
+ // Replace the entire HTML document with the fetched content
165
+ document.open();
166
+ document.write(html);
167
+ document.close();
168
+ })
169
+ .catch(error => {
170
+ console.error('Error fetching current page content:', error);
171
+ });
172
+ }
173
+ </script>
174
+
175
+ </body>
176
+ </html>
177
+ `;
178
+ }
179
+
180
+ // Function to generate sidebar links based on directory structure
181
+ function generateSidebar(rootDir) {
182
+ const generateLinks = (dir, baseUrl, depth = 0) => {
183
+ let links = '';
184
+ const items = fs.readdirSync(dir);
185
+
186
+ items.forEach(item => {
187
+ const itemPath = path.join(dir, item);
188
+ const relativePath = path.relative(rootDir, itemPath);
189
+ const urlPath = baseUrl + '/' + relativePath.replace(/\\/g, '/').replace('.md', '');
190
+
191
+ if (fs.statSync(itemPath).isDirectory()) {
192
+ // If directory, recursively generate nested list with increased depth
193
+ links += `<li class="font-bold ml-${depth * 2}">${item}</li>\n`;
194
+ links += `<ul class="ml-4 space-y-2">${generateLinks(itemPath, baseUrl, depth + 1)}</ul>\n`;
195
+ } else if (item.endsWith('.md')) {
196
+ // If Markdown file, generate list item with link
197
+ links += `<li class="ml-${depth * 2}"><a href="${urlPath}" class="text-blue-500 hover:underline">${item.replace('.md', '')}</a></li>\n`;
198
+ }
199
+ });
200
+
201
+ return links;
202
+ };
203
+
204
+ // Start generating sidebar from root directory
205
+ return generateLinks(rootDir, '');
206
+ }
207
+
208
+ // Create HTTP server
209
+ const server = http.createServer((req, res) => {
210
+ let filePath = '.' + req.url;
211
+
212
+ if (filePath === './' || filePath === './index.html') {
213
+ filePath = './README.md'; // Default to README.md for root URL
214
+ } else if (!path.extname(filePath)) {
215
+ // Append '.md' for paths that do not have an extension
216
+ filePath += '.md';
217
+ }
218
+
219
+ filePath = path.join(__dirname, 'markdown', filePath.replace(/^\//, ''));
220
+
221
+ fs.readFile(filePath, (err, data) => {
222
+ if (err) {
223
+ if (err.code === 'ENOENT') {
224
+ res.writeHead(404, { 'Content-Type': 'text/html' });
225
+ res.end('<h1>404 Not Found</h1><p>The requested URL was not found on this server.</p>');
226
+ } else {
227
+ res.writeHead(500, { 'Content-Type': 'text/html' });
228
+ res.end('<h1>500 Internal Server Error</h1><p>Sorry, something went wrong.</p>');
229
+ }
230
+ } else {
231
+ let contentType = 'text/html';
232
+
233
+ if (filePath.endsWith('.md')) {
234
+ // Convert Markdown to HTML
235
+ const markdownContent = data.toString();
236
+ htmlContent = convertMarkdownToHtml(markdownContent, filePath);
237
+
238
+ // Serve HTML with proper Tailwind CSS and sidebar
239
+ res.writeHead(200, { 'Content-Type': 'text/html' });
240
+ res.end(htmlContent);
241
+ } else {
242
+ // Serve other file types directly (e.g., CSS files)
243
+ res.writeHead(200, { 'Content-Type': contentType });
244
+ res.end(data);
245
+ }
246
+ }
247
+ });
248
+ });
249
+
250
+ // Attach Socket.IO to the HTTP server
251
+ const io = socketio(server);
252
+
253
+ // Watch changes in the Markdown directory
254
+ const markdownDir = path.join(__dirname, 'markdown');
255
+ if (fs.existsSync(markdownDir)) {
256
+ fs.watch(markdownDir, { recursive: true }, (eventType, filename) => {
257
+ console.log(`File ${filename} ${eventType}`);
258
+ io.emit('fileChange'); // Emit event to clients when file changes
259
+ });
260
+ } else {
261
+ console.error('Markdown directory does not exist.');
262
+ }
263
+
264
+ const port = process.env.PORT || 3000;
265
+ server.listen(port, () => {
266
+ console.log(`Server is running on http://localhost:${port}`);
267
+ });