portosaurus 1.14.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.
Files changed (57) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +116 -0
  3. package/bin/portosaurus.js +337 -0
  4. package/internal/notes/index.md +10 -0
  5. package/internal/sidebars.js +20 -0
  6. package/internal/src/components/AboutSection/index.js +67 -0
  7. package/internal/src/components/AboutSection/styles.module.css +492 -0
  8. package/internal/src/components/ContactSection/index.js +94 -0
  9. package/internal/src/components/ContactSection/styles.module.css +327 -0
  10. package/internal/src/components/ExperienceSection/index.js +25 -0
  11. package/internal/src/components/ExperienceSection/styles.module.css +180 -0
  12. package/internal/src/components/HeroSection/index.js +61 -0
  13. package/internal/src/components/HeroSection/styles.module.css +471 -0
  14. package/internal/src/components/NoteIndex/index.js +127 -0
  15. package/internal/src/components/NoteIndex/styles.module.css +143 -0
  16. package/internal/src/components/ProjectsSection/index.js +529 -0
  17. package/internal/src/components/ProjectsSection/styles.module.css +830 -0
  18. package/internal/src/components/ScrollToTop/index.js +98 -0
  19. package/internal/src/components/ScrollToTop/styles.module.css +96 -0
  20. package/internal/src/components/SocialLinks/index.js +129 -0
  21. package/internal/src/components/SocialLinks/styles.module.css +55 -0
  22. package/internal/src/components/Tooltip/index.js +30 -0
  23. package/internal/src/components/Tooltip/styles.module.css +92 -0
  24. package/internal/src/config/iconMappings.js +329 -0
  25. package/internal/src/config/metaTags.js +240 -0
  26. package/internal/src/config/prism.js +179 -0
  27. package/internal/src/config/sidebar.js +20 -0
  28. package/internal/src/css/bootstrap.css +6 -0
  29. package/internal/src/css/catppuccin.css +632 -0
  30. package/internal/src/css/custom.css +186 -0
  31. package/internal/src/css/tasks.css +868 -0
  32. package/internal/src/pages/index.js +98 -0
  33. package/internal/src/pages/notes.js +88 -0
  34. package/internal/src/pages/tasks.js +310 -0
  35. package/internal/src/utils/HashNavigation.js +250 -0
  36. package/internal/src/utils/appVersion.js +27 -0
  37. package/internal/src/utils/compileConfig.js +82 -0
  38. package/internal/src/utils/cssUtils.js +99 -0
  39. package/internal/src/utils/filterEnabledItems.js +21 -0
  40. package/internal/src/utils/generateFavicon.js +256 -0
  41. package/internal/src/utils/generateRobotsTxt.js +97 -0
  42. package/internal/src/utils/iconExtractor.js +159 -0
  43. package/internal/src/utils/imageDownloader.js +88 -0
  44. package/internal/src/utils/imageProcessor.js +134 -0
  45. package/internal/src/utils/linkShortner.js +0 -0
  46. package/internal/src/utils/updateTitle.js +107 -0
  47. package/package.json +51 -0
  48. package/template/.github/workflows/deploy.yml +57 -0
  49. package/template/README.md +70 -0
  50. package/template/blog/authors.yml +5 -0
  51. package/template/blog/welcome.md +10 -0
  52. package/template/config.js +233 -0
  53. package/template/notes/getting-started.md +7 -0
  54. package/template/static/README.md +33 -0
  55. package/utils/createConfig.js +227 -0
  56. package/utils/logger.js +19 -0
  57. package/utils/packageManager.js +88 -0
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/soymadip/portosaurus/refs/heads/compiler/static/img/icon.png" width=150>
3
+ <h1>Portosaurus</h1>
4
+ <p>Complete portfolio cum personal website solution for your digital personality.</p>
5
+ </div>
6
+
7
+ <br/>
8
+
9
+ ## 🧩 Features
10
+
11
+ - **📝 Full-featured Portfolio Site** — Showcase your work, skills, experience, social identity.
12
+ - **🎨 Beautiful UI** — Responsive design with Catppuccin color scheme.
13
+ - **🖼️ Project Showcase** — Interactive project cards with support for featured projects, project status badges, and tags
14
+ - **📚 Knowledge Base** — Integrated notes system with custom icons and categorization
15
+ - **✏️ Blog Platform** — Built-in blogging capabilities.
16
+ - **📋 Task Tracking** — Track your plans with priority levels and completion status
17
+ - **🛠️ Highly Configurable** — Extensive customization options in a central config file
18
+ - **🔍 Powerful Search** — Integrated search functionality for notes and blog content
19
+ - **📱 Mobile-Friendly** — Fully responsive design works on all devices
20
+ - **🚀 Auto Deployment** — Ready for GitHub Pages or any static hosting
21
+
22
+ <br/>
23
+
24
+ ## � Getting Started
25
+
26
+ ### 1. Create a New Project
27
+
28
+ Run the initialization command to set up your portfolio:
29
+
30
+ ```bash
31
+ # With Bun
32
+ bunx portosaurus init my-portfolio
33
+
34
+ # with npm
35
+ npx portosaurus init my-portfolio
36
+ ```
37
+
38
+ **Options:**
39
+
40
+ - `-gh`: Automatically sets up GitHub Actions workflow for deployment.
41
+ - `--install`: Installs dependencies automatically.
42
+
43
+ ### 2. Start Development
44
+
45
+ Navigate to your project and start the development server:
46
+
47
+ ```bash
48
+ cd my-portfolio
49
+
50
+ # With npm
51
+ npm run start
52
+
53
+ # With Bun
54
+ bun run start
55
+ ```
56
+
57
+ Your site will be available at `http://localhost:3000`.
58
+
59
+ ### 3. Build & Deploy
60
+
61
+ To generate the static site for production:
62
+
63
+ ```bash
64
+ # With Bun
65
+ bun run build
66
+
67
+ # With npm
68
+ npm run build
69
+ ```
70
+
71
+ The output will be in the `build/` directory.
72
+
73
+ #### GitHub Pages (Automated)
74
+
75
+ If you used the `-gh` flag during init, just push to your repository:
76
+
77
+ 1. Create a repository on GitHub.
78
+ 2. Push your code:
79
+ ```bash
80
+ git remote add origin https://github.com/username/repo.git
81
+ git branch -M main
82
+ git push -u origin main
83
+ ```
84
+ 3. Go to GitHub Settings > Pages > Build and deployment > Source > **GitHub Actions**.
85
+
86
+ <br>
87
+
88
+ ## ⚙️ Configuration
89
+
90
+ Portosaurus is configured via `config.js` in your project root.
91
+
92
+ ```javascript
93
+ module.exports = {
94
+ usrConf: {
95
+ hero_section: {
96
+ title: "Your Name",
97
+ description: "Software Engineer",
98
+ // ...
99
+ },
100
+ // ...
101
+ },
102
+ };
103
+ ```
104
+
105
+ <br>
106
+
107
+ ## 📄 Credits
108
+
109
+ - [Docusaurus](https://docusaurus.io/) - The static site builder framework this is built upon.
110
+ - [React](https://react.dev) - UI library for building the interactive components.
111
+ - [React Icons](https://react-icons.github.io/) - Icon library used throughout the site.
112
+ - Libraries listed in [package.json](https://github.com/soymadip/portosaurus/blob/compiler/package.json#L16) - Essential dependencies.
113
+ - [Hugo Profile](https://hugo-profile.netlify.app/) - Design inspiration.
114
+ - [Catppuccin](https://github.com/catppuccin/catppuccin) - Color scheme that inspired the site's palette.
115
+ - [Deepseek R1](https://www.deepseek.com/) hosted using [Ollama](https://ollama.com/library/deepseek-r1) - prism.js theme & project card component.
116
+ - Countless Internet forum posts - Filling me with information.
@@ -0,0 +1,337 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import fs from "fs-extra";
5
+ const {
6
+ readFileSync,
7
+ writeFileSync,
8
+ existsSync,
9
+ mkdirSync,
10
+ mkdirpSync,
11
+ readdirSync,
12
+ copySync,
13
+ removeSync,
14
+ } = fs;
15
+ import path from "path";
16
+ import chalk from "chalk";
17
+ import { fileURLToPath } from "url";
18
+ import { spawn, execSync } from "child_process";
19
+ import { createRequire } from "module";
20
+
21
+ import { logger } from "../utils/logger.js";
22
+ import { createConfig } from "../utils/createConfig.js";
23
+
24
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
25
+
26
+ const packageJson = JSON.parse(
27
+ readFileSync(new URL("../package.json", import.meta.url)),
28
+ );
29
+
30
+ const program = new Command();
31
+
32
+ program
33
+ .name("portosaurus")
34
+ .description("CLI for Portosaurus - The complete portfolio solution")
35
+ .version(packageJson.version);
36
+
37
+ // ----------- HELPERS -------------
38
+
39
+ async function prepareDocusaurusRun(projectRoot) {
40
+ const configPath = path.join(projectRoot, "config.js");
41
+
42
+ if (!existsSync(configPath)) {
43
+ console.log();
44
+ logger.error(
45
+ "config.js not found. Are you in a Portosaurus project directory?",
46
+ );
47
+ process.exit(1);
48
+ }
49
+
50
+ // Ensure internal files are synced to .portosaurus
51
+ const internalSrc = path.join(__dirname, "../internal/src");
52
+ const internalSidebars = path.join(__dirname, "../internal/sidebars.js");
53
+ const internalNotesIndex = path.join(__dirname, "../internal/notes/index.md");
54
+
55
+ const dotPorto = path.join(projectRoot, ".portosaurus");
56
+ const userSrc = path.join(dotPorto, "src");
57
+ const userSidebars = path.join(dotPorto, "sidebars.js");
58
+
59
+ // Ensure .portosaurus exists
60
+ fs.ensureDirSync(dotPorto);
61
+
62
+ // Always copy/overwrite src/
63
+ fs.copySync(internalSrc, userSrc);
64
+
65
+ // Always copy/overwrite sidebars.js
66
+ fs.copySync(internalSidebars, userSidebars);
67
+
68
+ // Ensure user has essential folders
69
+ const userRootNotes = path.join(projectRoot, "notes");
70
+ const userRootBlog = path.join(projectRoot, "blog");
71
+ const userRootStatic = path.join(projectRoot, "static");
72
+
73
+ fs.ensureDirSync(userRootNotes);
74
+ fs.ensureDirSync(userRootBlog);
75
+ fs.ensureDirSync(userRootStatic);
76
+
77
+ // Always ensure index.md exists (framework-managed file)
78
+ const userNotesIndex = path.join(userRootNotes, "index.md");
79
+ if (!fs.existsSync(userNotesIndex)) {
80
+ fs.copySync(internalNotesIndex, userNotesIndex);
81
+ }
82
+
83
+ // Load user config
84
+ const require = createRequire(import.meta.url);
85
+
86
+ let userConfig;
87
+ try {
88
+ userConfig = require(configPath);
89
+ } catch (e) {
90
+ logger.error(`Failed to load config.js: ${e.message}`);
91
+ process.exit(1);
92
+ }
93
+
94
+ // Ensure defaults if missing
95
+ if (!userConfig.usrConf) {
96
+ userConfig.usrConf = {};
97
+ }
98
+ if (!userConfig.usrConf.site_url) {
99
+ userConfig.usrConf.site_url = "auto";
100
+ }
101
+ if (!userConfig.usrConf.site_path) {
102
+ userConfig.usrConf.site_path = "auto";
103
+ }
104
+
105
+ // Generate Docusaurus Config
106
+ const docusaurusConfig = createConfig(userConfig, projectRoot);
107
+
108
+ // Write temp config file INSIDE .portosaurus
109
+ const tempConfigPath = path.join(dotPorto, "docusaurus.config.js");
110
+
111
+ const configContent = `// Auto-generated by Portosaurus
112
+ module.exports = ${JSON.stringify(docusaurusConfig, null, 2)};`;
113
+
114
+ writeFileSync(tempConfigPath, configContent);
115
+ logger.success("Generated Docusaurus config in .portosaurus.");
116
+
117
+ return { dotPorto };
118
+ }
119
+
120
+ async function runDocusaurus(command, args, dotPorto, projectRoot) {
121
+ // Detect package manager and find docusaurus binary
122
+ const { findDocusaurusBin } = await import("../utils/packageManager.js");
123
+ const docusaurus = findDocusaurusBin(projectRoot || process.cwd());
124
+
125
+ logger.info(
126
+ `Running docusaurus ${command} (via ${docusaurus.packageManager})`,
127
+ );
128
+
129
+ const child = spawn(
130
+ docusaurus.command,
131
+ [...docusaurus.args, command, dotPorto, ...args],
132
+ {
133
+ stdio: "inherit",
134
+ cwd: projectRoot,
135
+ env: { ...process.env, FORCE_COLOR: "true" },
136
+ },
137
+ );
138
+
139
+ return new Promise((resolve, reject) => {
140
+ child.on("error", (err) => {
141
+ logger.error(`Failed to run docusaurus ${command}: ${err.message}`);
142
+ reject(err);
143
+ });
144
+
145
+ child.on("close", (code) => {
146
+ if (code === 0) resolve();
147
+ else {
148
+ process.exit(code);
149
+ }
150
+ });
151
+ });
152
+ }
153
+
154
+ // --- INIT COMMAND ---
155
+
156
+ program
157
+ .command("init")
158
+ .description("Initialize a new Portosaurus project")
159
+ .argument("<project-name>", "Name of the project directory")
160
+ .option("-ngh, --no-github-pages", "Skip GitHub Pages deployment setup")
161
+ .option("-ni, --no-install", "Skip dependency installation")
162
+ .action(async (projectName, options) => {
163
+ const projectPath = path.resolve(process.cwd(), projectName);
164
+
165
+ if (existsSync(projectPath)) {
166
+ logger.error(`Directory ${projectName} already exists.`);
167
+ process.exit(1);
168
+ }
169
+
170
+ logger.info(
171
+ `Creating new Portosaurus project in ${chalk.bold(projectName)}...`,
172
+ );
173
+
174
+ try {
175
+ // Create project directory
176
+ mkdirSync(projectPath);
177
+
178
+ // Copy template files
179
+ const templatePath = path.resolve(__dirname, "../template");
180
+
181
+ // Check if template exists (it might not be populated yet)
182
+ if (!existsSync(templatePath)) {
183
+ logger.warn("Template directory not found. Creating basic structure.");
184
+
185
+ // Fallback basic creation if template missing (during dev)
186
+ mkdirpSync(path.join(projectPath, "blog"));
187
+ mkdirpSync(path.join(projectPath, "notes"));
188
+ mkdirpSync(path.join(projectPath, "static"));
189
+ writeFileSync(
190
+ path.join(projectPath, "config.js"),
191
+ "module.exports = { /* TODO */ };",
192
+ );
193
+ } else {
194
+ // Copy all template files
195
+ const templateFiles = readdirSync(templatePath);
196
+
197
+ for (const file of templateFiles) {
198
+ const srcPath = path.join(templatePath, file);
199
+ const destPath = path.join(projectPath, file);
200
+ copySync(srcPath, destPath);
201
+ }
202
+ }
203
+
204
+ // Create package.json
205
+ const packageJsonContent = {
206
+ name: projectName,
207
+ version: "0.0.1",
208
+ private: true,
209
+ scripts: {
210
+ start: "portosaurus start",
211
+ build: "portosaurus build",
212
+ },
213
+ dependencies: {
214
+ portosaurus: "latest",
215
+ },
216
+ };
217
+
218
+ writeFileSync(
219
+ path.join(projectPath, "package.json"),
220
+ JSON.stringify(packageJsonContent, null, 2),
221
+ );
222
+
223
+ // Git init
224
+ try {
225
+ execSync("git init", { cwd: projectPath, stdio: "ignore" });
226
+ } catch (e) {
227
+ logger.warn("Failed to initialize git repository.");
228
+ }
229
+
230
+ // Remove GitHub Pages files if --no-github-pages flag is set
231
+ if (options.githubPages === false) {
232
+ const githubDir = path.join(projectPath, ".github");
233
+ if (existsSync(githubDir)) {
234
+ removeSync(githubDir);
235
+ logger.info("Skipped GitHub Pages setup");
236
+ }
237
+ } else {
238
+ // GitHub Pages is enabled by default
239
+ // Create .nojekyll in static directory
240
+ const nojekyllPath = path.join(projectPath, "static", ".nojekyll");
241
+ writeFileSync(nojekyllPath, "");
242
+
243
+ logger.success("GitHub Pages deployment configured");
244
+ logger.tip(
245
+ "Enable GitHub Pages: Settings > Pages > Source: GitHub Actions",
246
+ );
247
+ }
248
+
249
+ logger.success(`Success! Created ${projectName} at ${projectPath}`);
250
+
251
+ // Detect package manager for install instructions
252
+ const { detectPackageManager } =
253
+ await import("../utils/packageManager.js");
254
+ const pm = detectPackageManager(projectPath) || "npm";
255
+ const installCmd = pm === "npm" ? "npm install" : `${pm} install`;
256
+ const runCmd = pm === "npm" ? "npm run" : `${pm} run`;
257
+
258
+ if (options.install !== false) {
259
+ logger.info(`Running ${installCmd}...`);
260
+ try {
261
+ execSync(installCmd, {
262
+ cwd: projectPath,
263
+ stdio: "inherit",
264
+ });
265
+
266
+ logger.success("Dependencies installed!");
267
+ logger.tip("You can now run:");
268
+ logger.info(` cd ${projectName}`);
269
+ logger.info(` ${runCmd} start`);
270
+ } catch (error) {
271
+ logger.error("Failed to install dependencies.");
272
+ logger.tip("You can install them manually:");
273
+ logger.info(` cd ${projectName}`);
274
+ logger.info(` ${installCmd}`);
275
+ }
276
+ } else {
277
+ logger.tip("Inside that directory, you can run:");
278
+ logger.info(` cd ${projectName}`);
279
+ logger.info(` ${installCmd}`);
280
+ logger.info(` ${runCmd} start`);
281
+ }
282
+ } catch (error) {
283
+ logger.error(`Failed to initialize project: ${error.message}`);
284
+ process.exit(1);
285
+ }
286
+ });
287
+
288
+ // --- START COMMAND ---
289
+
290
+ program
291
+ .command("start")
292
+ .description("Start the development server")
293
+ .action(async () => {
294
+ const projectRoot = process.cwd();
295
+ logger.info("Starting development server...");
296
+ try {
297
+ const { dotPorto } = await prepareDocusaurusRun(projectRoot);
298
+ await runDocusaurus("start", [], dotPorto, projectRoot);
299
+ } catch (error) {
300
+ logger.error(`Failed to start: ${error.message}`);
301
+ process.exit(1);
302
+ }
303
+ });
304
+
305
+ // --- BUILD COMMAND ---
306
+
307
+ program
308
+ .command("build")
309
+ .description("Build the static site")
310
+ .action(async () => {
311
+ const projectRoot = process.cwd();
312
+ logger.info("Building Portosaurus site...");
313
+
314
+ try {
315
+ const { dotPorto } = await prepareDocusaurusRun(projectRoot);
316
+
317
+ const buildDir = path.join(projectRoot, "build");
318
+ const args = ["--out-dir", buildDir];
319
+
320
+ await runDocusaurus("build", args, dotPorto, projectRoot);
321
+
322
+ // Create .nojekyll for GitHub Pages compatibility
323
+ const nojekyllPath = path.join(buildDir, ".nojekyll");
324
+ if (existsSync(buildDir)) {
325
+ writeFileSync(nojekyllPath, "");
326
+ logger.info("Created .nojekyll");
327
+ }
328
+
329
+ logger.success("Build completed successfully!");
330
+ logger.info(`Output directory: ${buildDir}`);
331
+ } catch (error) {
332
+ logger.error(`Failed to build: ${error.message}`);
333
+ process.exit(1);
334
+ }
335
+ });
336
+
337
+ program.parse();
@@ -0,0 +1,10 @@
1
+ ---
2
+ sidebar_position: 0
3
+ id: index
4
+ ---
5
+
6
+ # Notes
7
+
8
+ Welcome to your notes section! This is your knowledge base.
9
+
10
+ Browse the sidebar to explore your notes, or start creating new ones.
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Default sidebars configuration for Portosaurus
3
+ * This file is auto-generated and copied to user projects
4
+ */
5
+
6
+ module.exports = {
7
+ notes: [
8
+ {
9
+ type: "category",
10
+ label: "Notes",
11
+ link: { type: "doc", id: "index" },
12
+ items: [
13
+ {
14
+ type: "autogenerated",
15
+ dirName: ".",
16
+ },
17
+ ],
18
+ },
19
+ ],
20
+ };
@@ -0,0 +1,67 @@
1
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
2
+ import styles from './styles.module.css';
3
+
4
+ export default function AboutSection({ id, className}) {
5
+ const { siteConfig } = useDocusaurusContext();
6
+ const { customFields } = siteConfig;
7
+ const aboutMe = customFields.aboutMe || {};
8
+
9
+ return (
10
+ <div id={id} className={`${styles.aboutSection} ${className || ''}`} role="region" aria-label="About me section">
11
+ <div className={styles.aboutContainer}>
12
+ <div className={styles.aboutHeader}>
13
+ <h2 className={styles.aboutHeading}>{"About Me"}</h2>
14
+ </div>
15
+
16
+ <div className={styles.aboutContent}>
17
+ <div className={styles.aboutBio}>
18
+ <div className={styles.bioImageContainer}>
19
+ {aboutMe.image && (
20
+ <div className={styles.imageWrapper}>
21
+ <img
22
+ src={aboutMe.image}
23
+ alt="About Me"
24
+ className={styles.aboutImage}
25
+ loading="lazy"
26
+ />
27
+ </div>
28
+ )}
29
+ </div>
30
+
31
+ <div className={styles.bioTextContainer}>
32
+ <div className={styles.bioText}>
33
+ {Array.isArray(aboutMe.description) ? (
34
+ aboutMe.description.map((paragraph, index) => (
35
+ <p key={index} className={styles.aboutParagraph}>{paragraph}</p>
36
+ ))
37
+ ) : (
38
+ <p className={styles.aboutParagraph}>
39
+ {aboutMe.description || "Information about me goes here."}
40
+ </p>
41
+ )}
42
+ </div>
43
+
44
+ {aboutMe.skills && aboutMe.skills.length > 0 && (
45
+ <div className={styles.skillsContainer}>
46
+ <h3 className={styles.skillsTitle} id="skills-heading">My Skills</h3>
47
+ <div className={styles.skillsGrid} role="list">
48
+ {aboutMe.skills.map((skill, index) => (
49
+ <div
50
+ key={index}
51
+ className={styles.skillBadge}
52
+ role="listitem"
53
+ style={{ animationDelay: `${index * 0.05}s` }}
54
+ >
55
+ {skill}
56
+ </div>
57
+ ))}
58
+ </div>
59
+ </div>
60
+ )}
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ );
67
+ }