portosaurus 3.0.1 → 4.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.
Files changed (60) hide show
  1. package/README.md +26 -126
  2. package/bin/portosaurus.mjs +8 -0
  3. package/package.json +6 -3
  4. package/src/assets/img/icon.png +0 -0
  5. package/src/assets/img/{icon.svg → svg/icon.svg} +35 -37
  6. package/src/assets/img/svg/project-blank.svg +140 -0
  7. package/src/assets/sample-resume.pdf +0 -0
  8. package/src/cli/build.mjs +2 -5
  9. package/src/cli/dev.mjs +27 -5
  10. package/src/cli/init.mjs +7 -14
  11. package/src/cli/schema.mjs +211 -0
  12. package/src/core/buildDocuConfig.mjs +306 -191
  13. package/src/core/constants.mjs +7 -1
  14. package/src/template/config.yml +150 -0
  15. package/src/template/notes/welcome.mdx +6 -0
  16. package/src/template/package.json +3 -3
  17. package/src/theme/MDXComponents.js +0 -1
  18. package/src/theme/components/AboutSection/index.js +32 -17
  19. package/src/theme/components/AboutSection/styles.module.css +151 -344
  20. package/src/theme/components/ContactSection/index.js +23 -14
  21. package/src/theme/components/ContactSection/styles.module.css +19 -8
  22. package/src/theme/components/ExperienceSection/index.js +12 -5
  23. package/src/theme/components/HeroSection/index.js +4 -3
  24. package/src/theme/components/HeroSection/styles.module.css +17 -16
  25. package/src/theme/components/NavArrow/index.js +114 -0
  26. package/src/theme/components/NavArrow/styles.module.css +107 -0
  27. package/src/theme/components/NoteIndex/index.js +66 -95
  28. package/src/theme/components/NoteIndex/styles.module.css +85 -89
  29. package/src/theme/components/Preview/components/FeedbackStates.js +3 -1
  30. package/src/theme/components/Preview/components/PreviewContent.js +91 -0
  31. package/src/theme/components/Preview/components/PreviewHeader.js +41 -33
  32. package/src/theme/components/Preview/components/Triggers/Pv.js +129 -72
  33. package/src/theme/components/Preview/components/ViewerWindow.js +198 -234
  34. package/src/theme/components/Preview/hooks/useAdaptiveSizing.js +115 -0
  35. package/src/theme/components/Preview/hooks/useDeepLinkHash.js +18 -23
  36. package/src/theme/components/Preview/hooks/useDockLayout.js +48 -8
  37. package/src/theme/components/Preview/hooks/useTouchZoom.js +118 -0
  38. package/src/theme/components/Preview/renderers/CodeRenderer.js +64 -25
  39. package/src/theme/components/Preview/state/index.js +70 -17
  40. package/src/theme/components/Preview/styles.module.css +181 -45
  41. package/src/theme/components/Preview/utils/index.js +11 -10
  42. package/src/theme/components/ProjectsSection/index.js +138 -148
  43. package/src/theme/components/ProjectsSection/styles.module.css +178 -112
  44. package/src/theme/components/SocialLinks/index.js +9 -7
  45. package/src/theme/components/Tooltip/index.js +31 -20
  46. package/src/theme/components/Tooltip/styles.module.css +101 -38
  47. package/src/theme/config/iconMappings.js +2 -0
  48. package/src/theme/css/custom.css +72 -0
  49. package/src/theme/hooks/useScrollReveal.js +30 -0
  50. package/src/theme/pages/index.js +7 -27
  51. package/src/theme/pages/notes.js +2 -2
  52. package/src/theme/pages/tasks.js +12 -11
  53. package/src/utils/cliUtils.mjs +23 -51
  54. package/src/utils/configUtils.mjs +95 -84
  55. package/src/utils/systemUtils.mjs +171 -0
  56. package/src/template/config.js +0 -68
  57. package/src/theme/components/ScrollToTop/index.js +0 -95
  58. package/src/theme/components/ScrollToTop/styles.module.css +0 -97
  59. package/src/theme/config/metaTags.js +0 -21
  60. /package/src/template/{.nojekyll → static/.nojekyll} +0 -0
@@ -1,208 +1,296 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { fileURLToPath } from "url";
4
- import { createRequire } from "module";
5
- import { logger } from "../utils/logger.mjs";
6
-
3
+ import { PortoRoot, Paths, PortoPkg } from "./constants.mjs";
4
+ import {
5
+ loadPkg,
6
+ getGitDate,
7
+ useEnabled,
8
+ getNestedValue,
9
+ } from "../utils/systemUtils.mjs";
7
10
  import {
8
- deepMerge,
9
11
  resolveVars,
10
12
  resolveSiteUrl,
11
13
  resolveBasePath,
12
- getVersion,
13
- useEnabled,
14
14
  createStaticAssetResolver,
15
+ buildHeadTags,
15
16
  } from "../utils/configUtils.mjs";
16
- import { PortoRoot } from "./constants.mjs";
17
-
18
- // ─── Main Config Generator ─────────────────────────────────
19
-
20
- /**
21
- * Creates a Docusaurus config from a user's Portosaurus config.
22
- *
23
- * @param {Object} rawUserConfig - The user's config object (export of config.js).
24
- * May be { usrConf: {...} } or just {...}.
25
- * @param {string} UserRoot - Absolute path to the user's project root.
26
- * @returns {Object} Complete Docusaurus configuration object.
27
- */
28
- export function buildDocuConfig(rawUserConfig, UserRoot) {
29
- // Support both { usrConf: {...} } and direct config objects
30
- const UserConfigRaw = rawUserConfig.usrConf ?? rawUserConfig;
31
-
32
- // Load master template defaults for fallback resolution
33
- const require = createRequire(import.meta.url);
34
- const templateDefaults = require("../template/config.js").usrConf;
35
-
36
- // Static directories — defined early so aliases can reference them
17
+
18
+ export function buildDocuConfig(UserConfigRaw, UserRoot) {
19
+ // Get user Project Version
20
+ const usrProjectVer = loadPkg(UserRoot).version || "0.0.0";
21
+
22
+ const portoVersion = PortoPkg.version || "0.0.0";
23
+ const portoRepo = (PortoPkg.repository?.url || "").replace(/\.git$/, "");
24
+ const lastUpdated = getGitDate(UserRoot);
25
+
26
+ // Static directories
37
27
  const UserStaticDir = path.resolve(UserRoot, "static");
38
- const PortoAssetDir = path.resolve(PortoRoot, "src/assets");
39
-
40
- // Hydrate the configuration by deep-merging user config over the template defaults.
41
- // This ensures that even if the user deletes nested fields, the structure remains safe.
42
- const merged = deepMerge(templateDefaults, UserConfigRaw);
43
-
44
- // Find Docusaurus root for reference
45
- let docuRoot = "";
46
- try {
47
- docuRoot = path.dirname(require.resolve("@docusaurus/core/package.json"));
48
- } catch {
49
- // If not found (e.g. during some build stages), fallback to node_modules relative to PortoRoot
50
- docuRoot = path.resolve(PortoRoot, "node_modules/@docusaurus/core");
51
- }
52
-
53
- // Resolve {{...}} template variables path prefixes in one pass
54
- const UserConfig = resolveVars(merged, merged, {
55
- siteRoot: UserRoot,
56
- portoRoot: PortoRoot,
57
- docuRoot: docuRoot,
58
- });
28
+ const PortoAssetDir = Paths.assets;
59
29
 
60
- // Compute derived values
61
- const siteUrl = resolveSiteUrl(UserConfig.site_url);
62
- const basePath = resolveBasePath(UserConfig.site_path);
63
- const version = getVersion();
64
-
65
- const UserProjName = UserConfig.hero_section.title;
66
-
67
- // Resolve internal paths
68
-
69
- // Prism themes
70
- let catppuccinMocha, catppuccinLatte;
71
- try {
72
- const prism = require(path.resolve(PortoRoot, "src/theme/config/prism.js"));
73
- catppuccinMocha = prism.catppuccinMocha;
74
- catppuccinLatte = prism.catppuccinLatte;
75
- } catch {
76
- catppuccinMocha = {};
77
- catppuccinLatte = {};
78
- }
79
-
80
- // Meta tags
81
- let PortoMetaTags = [];
82
- try {
83
- const meta = require(
84
- path.resolve(PortoRoot, "src/theme/config/metaTags.js"),
85
- );
86
- PortoMetaTags = meta.PortoMetaTags ?? meta.metaTags ?? [];
87
- } catch {
88
- // OK — no meta tags
89
- }
90
-
91
- // Paths for content — these point directly to the user's project
92
- const UserNotesDir = path.resolve(UserRoot, "notes");
93
- const UserBlogDir = path.resolve(UserRoot, "blog");
94
-
95
- const PortoFaviconCacheDir = path.resolve(
96
- UserRoot,
97
- ".docusaurus/portosaurus/favicon",
98
- );
99
- const StaticDirs = [
100
- UserStaticDir,
101
- PortoAssetDir,
102
- PortoFaviconCacheDir,
103
- ].filter((d) => fs.existsSync(d));
104
-
105
- /**
106
- * Validates a static asset path with fallback.
107
- * Aliases (@porto/, @site/) are already resolved globally by resolveAliases().
108
- * - Remote URLs (http/https) pass through as-is.
109
- * - Local paths are checked against UserStaticDir, then PortoAssetDir.
110
- */
30
+ // Resolve static asset path with fallback
111
31
  const resolveStaticAsset = createStaticAssetResolver(
32
+ UserRoot,
112
33
  UserStaticDir,
113
34
  PortoAssetDir,
114
35
  );
115
36
 
116
- // Pages
117
- const PortoPagesDir = path.resolve(PortoRoot, "src/theme/pages");
37
+ // Resolve SiteURL and BasePath for Docusaurus Root
38
+ const usrSiteUrl = resolveSiteUrl(UserConfigRaw.site?.url || "auto");
39
+ const ursSitePath = resolveBasePath(UserConfigRaw.site?.path || "auto");
118
40
 
119
- // Theme overrides
120
- const PortoThemeDir = path.resolve(PortoRoot, "src/theme");
41
+ // Resolve {{...}} template variables in user config
42
+ const UserConfig = resolveVars(UserConfigRaw, UserConfigRaw, {
43
+ siteRoot: UserRoot,
44
+ portoRoot: PortoRoot,
45
+ compileYear: new Date().getFullYear(),
46
+ compileDate: new Date().toLocaleDateString(),
47
+ portoVersion,
48
+ projectVersion: usrProjectVer,
49
+ siteUrl: usrSiteUrl,
50
+ baseUrl: ursSitePath,
51
+ lastUpdated,
52
+ isProd: process.env.NODE_ENV === "production",
53
+ isDev: process.env.NODE_ENV === "development",
54
+ nodeEnv: process.env.NODE_ENV || "development",
55
+ custom: UserConfigRaw.custom || {},
56
+ });
121
57
 
122
- // Sidebar config
123
- const sidebarPath = path.resolve(PortoRoot, "src/theme/config/sidebar.js");
58
+ // Helper to get nested values with fallbacks
59
+ const get = (...args) => getNestedValue(UserConfig, ...args);
124
60
 
125
- // CSS
126
- const PortoCustomCss = path.resolve(PortoRoot, "src/theme/css/custom.css");
61
+ // ─────────────────────── Build the Docusaurus config ───────────────────────
127
62
 
128
- // ───────────────────────Build the Docusaurus config ───────────────────────
63
+ /** The primary title of your website (e.g., your name or brand) */
64
+ const siteName = get("site.title", "Your Name");
65
+ const siteUrl = usrSiteUrl;
66
+ const sitePath = ursSitePath;
67
+
68
+ const UserNotesDir = "notes";
69
+ const UserBlogDir = "blog";
70
+ const UserTasksDir = "tasks";
129
71
 
130
72
  const config = {
131
- projectName: UserConfig.hero_section.title,
132
- title: UserConfig.hero_section.title,
133
- tagline: UserConfig.hero_section.description,
73
+ projectName: siteName,
74
+ title: siteName,
75
+
76
+ /** A short, catchy description shown below the title on the home page */
77
+ tagline: get(
78
+ "site.tagline",
79
+ "Short description about you, your passion, your goals etc.",
80
+ ),
81
+
82
+ url: siteUrl,
83
+ baseUrl: sitePath,
84
+
85
+ // Checks user's given favicon path -> internal favicon/favicon.ico -> img/icon.png
134
86
  favicon: resolveStaticAsset(
135
- UserConfig.favicon,
87
+ get("site.favicon", ""),
136
88
  resolveStaticAsset("favicon/favicon.ico", "img/icon.png"),
137
89
  ),
138
- url: siteUrl,
139
- baseUrl: basePath,
140
90
 
141
- organizationName: UserConfig.hero_section.title,
142
- deploymentBranch: "gh-pages",
143
- onBrokenAnchors: "ignore",
144
- onBrokenLinks: "warn",
91
+ organizationName: siteName,
145
92
 
93
+ /** Behavior when a broken anchor link is found (throw, warn, ignore) */
94
+ onBrokenAnchors: get("site.on_broken_anchors", "throw"),
95
+
96
+ /** Behavior when a broken internal link is found (throw, warn, ignore) */
97
+ onBrokenLinks: get("site.on_broken_links", "throw"),
98
+
99
+ // TODO: research & allow to configure this
146
100
  i18n: {
147
101
  defaultLocale: "en",
148
102
  locales: ["en"],
149
103
  },
150
104
 
151
- headTags: PortoMetaTags,
105
+ headTags: buildHeadTags([
106
+ {
107
+ meta: {
108
+ name: "msapplication-TileColor",
109
+ content: "var(--ifm-background-color)",
110
+ },
111
+ },
112
+ {
113
+ meta: {
114
+ name: "theme-color",
115
+ content: "var(--ifm-background-color)",
116
+ },
117
+ },
118
+ ...get("site.head_tags", []),
119
+ ]),
152
120
 
153
121
  customFields: {
154
- version,
122
+ portoVersion,
123
+
124
+ corsProxyList: [
125
+ ...[].concat(get("site.cors_proxy", [])),
126
+ "https://cors-proxy.soymadip.workers.dev/?url=",
127
+ "https://api.allorigins.win/raw?url=",
128
+ ].filter(Boolean),
155
129
 
156
130
  heroSection: {
157
131
  profilePic: resolveStaticAsset(
158
- UserConfig.hero_section.profile_pic,
132
+ get("home_page.hero.profile_pic", "site.favicon", ""),
159
133
  "img/icon.png",
160
134
  ),
161
- intro: UserConfig.hero_section.intro,
162
- title: UserConfig.hero_section.title,
163
- subtitle: UserConfig.hero_section.subtitle,
164
- profession: UserConfig.hero_section.profession,
165
- description: UserConfig.hero_section.description,
166
- learnMoreButtonTxt: UserConfig.hero_section.learn_more_button_txt,
135
+ intro: get("home_page.hero.intro", "Hello there, I'm"),
136
+ title: get("home_page.hero.title", "site.title", "Your Name"),
137
+ subtitle: get("home_page.hero.subtitle", "I am a"),
138
+ profession: get("home_page.hero.profession", "Your Profession"),
139
+ desc: get(
140
+ "home_page.hero.desc",
141
+ "site.tagline",
142
+ "Welcome to my portfolio.",
143
+ ),
144
+ learnMoreButtonTxt: get(
145
+ "home_page.hero.learn_more_btn_txt",
146
+ "Learn More",
147
+ ),
148
+ social: get("home_page.hero.social", null),
167
149
  },
168
150
 
169
151
  aboutMe: {
170
- enable: UserConfig.about_me.enable,
152
+ /** Enable or disable the About Me section entirely */
153
+ enable: get("home_page.about.enable", true),
154
+
155
+ /** Heading text for the About section */
156
+ heading: get("home_page.about.heading", "About Me"),
157
+
158
+ /** The main image for the About section */
171
159
  image: resolveStaticAsset(
172
- UserConfig.about_me.image ?? UserConfig.hero_section.profile_pic,
160
+ get(
161
+ "home_page.about.image",
162
+ "home_page.hero.profile_pic",
163
+ "site.favicon",
164
+ "",
165
+ ),
173
166
  "img/icon.png",
174
167
  ),
175
- description: UserConfig.about_me.description,
176
- skills: UserConfig.about_me.skills,
177
- resumeLink: UserConfig.about_me.resume_link,
168
+ /** A detailed bio or description about yourself (Array of strings for paragraphs) */
169
+ bio: get("home_page.about.bio", [
170
+ "I am a developer who loves turning ideas into reality.",
171
+ "With background in computer science and a passion for design, I bridge the gap between aesthetics and functionality.",
172
+ "Driven by curiosity and a commitment to excellence.",
173
+ ]),
174
+
175
+ /** A list of your professional skills or technologies */
176
+ skills: get("home_page.about.skills", [
177
+ "skill1",
178
+ "skill2",
179
+ "skill3",
180
+ "skill4",
181
+ ]),
182
+ /** The title shown above the skills list */
183
+ skillsHeading: get("home_page.about.skills_heading", "My Skills"),
184
+ /** Path to your professional resume (PDF or external link) */
185
+ resume: resolveStaticAsset(get("home_page.about.resume", null), null),
178
186
  },
179
187
 
180
- projects: UserConfig.project_shelf,
181
- experience: UserConfig.experience,
188
+ projects: {
189
+ /** Enable or disable the Projects section */
190
+ enable: get("home_page.project_shelf.enable", true),
191
+
192
+ /** Heading for the projects section */
193
+ heading: get("home_page.project_shelf.heading", "My Projects"),
194
+
195
+ /** Sub-heading or description for your project shelf */
196
+ subheading: get(
197
+ "home_page.project_shelf.subheading",
198
+ "A collection of all my works, with featured projects highlighted",
199
+ ),
200
+ /** Enable automatic scrolling for the project cards */
201
+ autoplay: get("home_page.project_shelf.autoplay", true),
202
+
203
+ /** The list of projects to display (Array of project objects) */
204
+ projects: get("home_page.project_shelf.projects", [
205
+ {
206
+ title: "Project One",
207
+ desc: "A brief description of your project and the tech used.",
208
+ tags: ["React", "Portosaurus"],
209
+ state: "active",
210
+ website: "https://example.com",
211
+ repo: "https://github.com/",
212
+ demo: "https://example.com/demo",
213
+ },
214
+ {
215
+ title: "Project Two",
216
+ icon: PortoRoot + "/img/icon.png",
217
+ featured: true,
218
+ desc: "Another awesome project you've built recently.",
219
+ tags: ["python", "tag2"],
220
+ state: "completed",
221
+ website: "https://example.com",
222
+ repo: "https://github.com/",
223
+ },
224
+ ]).map((p) => ({
225
+ ...p,
226
+ icon: resolveStaticAsset(p.icon, ""),
227
+ })),
228
+ },
229
+
230
+ // TODO
231
+ experience: {
232
+ /** Enable or disable the Experience timeline */
233
+ enable: get("home_page.experience.enable", false),
234
+
235
+ /** Heading for the experience section */
236
+ heading: get("home_page.experience.heading", "Experience"),
237
+
238
+ /** Sub-heading for your professional journey */
239
+ subheading: get(
240
+ "home_page.experience.subheading",
241
+ "My professional journey and work experience",
242
+ ),
243
+ /** List of work history items */
244
+ list: get("home_page.experience.list", []),
245
+ },
182
246
 
183
247
  socialLinks: {
184
- enable: UserConfig.social_links.enable,
185
- links: UserConfig.social_links.links,
248
+ /** Enable or disable the Social Links / Contact section */
249
+ enable: get("home_page.social.enable", true),
250
+
251
+ /** Heading for the contact section */
252
+ heading: get("home_page.social.heading", "Get In Touch"),
253
+
254
+ /** Brief call-to-action or invitation to contact you */
255
+ subheading: get(
256
+ "home_page.social.subheading",
257
+ "Feel free to reach out for collaborations, questions, or just to say hello!",
258
+ ),
259
+ /** List of social media profiles and contact methods */
260
+ links: get("home_page.social.links", [
261
+ {
262
+ name: "Email",
263
+ desc: "Send me an email",
264
+ url: "mailto://you@yourdomain.com",
265
+ },
266
+ {
267
+ name: "LinkedIn",
268
+ desc: "Connect on LinkedIn",
269
+ url: "https://www.linkedin.com/in/yourusername",
270
+ },
271
+ ]),
186
272
  },
187
273
 
188
274
  robotsTxt: {
189
- enable: UserConfig.robots_txt,
190
- rules: [{ disallow: ["/notes/", "/tasks/"] }],
191
- customLines: [],
275
+ /** Enable generation of robots.txt file */
276
+ enable: get("site.robots_txt.enable", true),
277
+ rules: [{ disallow: [`/${UserNotesDir}/`, `/${UserTasksDir}/`] }],
278
+
279
+ /** Additional custom lines to add to robots.txt */
280
+ customLines: get("site.robots_txt.custom_lines", []),
192
281
  },
193
282
 
194
283
  tasksPage: {
195
- enable: UserConfig.tasks_page.enable,
196
- title: UserConfig.tasks_page.title,
197
- description: UserConfig.tasks_page.description,
198
- taskList: UserConfig.tasks_page.tasks,
199
- },
284
+ /** Enable or disable the Tasks/TODO page */
285
+ enable: get("tasks.enable", false),
200
286
 
201
- corsProxyList: [
202
- ...[].concat(UserConfig.cors_proxy || []),
203
- "https://cors-proxy.soymadip.workers.dev/?url=",
204
- "https://api.allorigins.win/raw?url=",
205
- ].filter(Boolean),
287
+ /** Title for the Tasks page */
288
+ title: get("tasks.title", "Tasks"),
289
+
290
+ /** Subtitle for the Tasks page */
291
+ subtitle: get("tasks.subtitle", "My current focus and todo list"),
292
+ taskList: get("tasks.list", []),
293
+ },
206
294
  },
207
295
 
208
296
  presets: [
@@ -212,24 +300,24 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
212
300
  }),
213
301
  {
214
302
  docs: {
215
- routeBasePath: "notes",
303
+ routeBasePath: UserNotesDir,
216
304
  path: UserNotesDir,
217
- sidebarPath,
305
+ sidebarPath: path.resolve(PortoRoot, "src/theme/config/sidebar.js"),
218
306
  admonitions: {
219
- keywords: [
220
- "note",
221
- "tip",
222
- "info",
223
- "warning",
224
- "danger",
225
- "question",
226
- ],
227
307
  extendDefaults: true,
308
+ // keywords: [
309
+ // "note",
310
+ // "tip",
311
+ // "info",
312
+ // "warning",
313
+ // "danger",
314
+ // "question",
315
+ // ],
228
316
  },
229
317
  },
230
318
  blog: {
231
319
  path: UserBlogDir,
232
- feedOptions: UserConfig.rss
320
+ feedOptions: get("site.rss", true)
233
321
  ? { type: ["rss", "atom"], xslt: true }
234
322
  : undefined,
235
323
  showReadingTime: false,
@@ -238,10 +326,10 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
238
326
  onUntruncatedBlogPosts: "warn",
239
327
  },
240
328
  theme: {
241
- customCss: PortoCustomCss,
329
+ customCss: path.resolve(PortoRoot, "src/theme/css/custom.css"),
242
330
  },
243
331
  pages: {
244
- path: PortoPagesDir,
332
+ path: path.resolve(PortoRoot, "src/theme/pages"),
245
333
  },
246
334
  },
247
335
  ],
@@ -253,44 +341,51 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
253
341
  [
254
342
  path.resolve(PortoRoot, "src/plugins/theme.mjs"),
255
343
  {
256
- themeDir: PortoThemeDir,
344
+ themeDir: path.resolve(PortoRoot, "src/theme"),
257
345
  },
258
346
  ],
259
347
  ],
260
348
 
261
349
  markdown: {
262
- mermaid: true,
350
+ mermaid: get("theme.markdown.mermaid", true),
263
351
  hooks: {
264
- onBrokenMarkdownLinks: "warn",
352
+ onBrokenMarkdownLinks: get("theme.markdown.on_broken_links", "throw"),
353
+ onBrokenMarkdownImages: get("theme.markdown.on_broken_images", "throw"),
265
354
  },
266
355
  },
267
356
 
268
357
  themeConfig: {
269
- image: resolveStaticAsset(UserConfig.social_card, "img/social-card.jpeg"),
358
+ image: resolveStaticAsset(
359
+ get("site.social_card", ""),
360
+ "img/social-card.jpeg",
361
+ ),
362
+
270
363
  docs: {
271
364
  sidebar: {
272
- hideable: UserConfig.collapsable_sidebar,
365
+ hideable: get("theme.navigation.collapsable_sidebar", true),
273
366
  },
274
367
  },
368
+
275
369
  imageZoom: {
276
370
  options: {
277
371
  margin: 2,
278
372
  background: "rgba(var(--ifm-background-color-rgb), 0.9)",
279
373
  },
280
374
  },
375
+
281
376
  colorMode: {
282
- defaultMode: UserConfig.dark_mode ? "dark" : "light",
283
- disableSwitch: UserConfig.disable_theme_switch,
377
+ defaultMode: get("theme.appearance.dark_mode", true) ? "dark" : "light",
378
+ disableSwitch: get("theme.appearance.disable_switch", false),
284
379
  },
285
380
  navbar: {
286
- title: UserProjName,
287
- hideOnScroll: UserConfig.hide_navbar_on_scroll,
381
+ title: get("site.title", "Your Portfolio"),
382
+ hideOnScroll: get("theme.navigation.hide_navbar_on_scroll", true),
288
383
  logo: {
289
384
  alt: "Site Logo",
290
385
  src: resolveStaticAsset(
291
- UserConfig.favicon,
386
+ get("site.favicon", ""),
292
387
  resolveStaticAsset(
293
- UserConfig.hero_section.profile_pic,
388
+ get("home_page.hero.profile_pic", ""),
294
389
  "img/icon.png",
295
390
  ),
296
391
  ),
@@ -302,7 +397,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
302
397
  className: "navbar-search-bar",
303
398
  },
304
399
  {
305
- enable: UserConfig.about_me.enable,
400
+ enable: get("home_page.about.enable", true),
306
401
  value: {
307
402
  label: "About Me",
308
403
  to: "/#about",
@@ -311,7 +406,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
311
406
  },
312
407
  },
313
408
  {
314
- enable: UserConfig.project_shelf.enable,
409
+ enable: get("home_page.project_shelf.enable", true),
315
410
  value: {
316
411
  label: "Projects",
317
412
  to: "/#projects",
@@ -320,7 +415,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
320
415
  },
321
416
  },
322
417
  {
323
- enable: UserConfig.experience.enable,
418
+ enable: get("home_page.experience.enable", false),
324
419
  value: {
325
420
  label: "Experience",
326
421
  to: "/#experience",
@@ -329,7 +424,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
329
424
  },
330
425
  },
331
426
  {
332
- enable: UserConfig.social_links.enable,
427
+ enable: get("home_page.social.enable", true),
333
428
  value: {
334
429
  label: "Contact",
335
430
  to: "/#contact",
@@ -343,18 +438,20 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
343
438
  position: "right",
344
439
  className: "_navbar-more-items",
345
440
  items: useEnabled([
346
- { label: "Notes", to: "/notes" },
347
- { label: "Blog", to: "/blog" },
441
+ { label: "Notes", to: `/${UserNotesDir}` },
442
+ { label: "Blog", to: `/${UserBlogDir}` },
348
443
  {
349
- enable: UserConfig.tasks_page.enable,
350
- value: { label: "Tasks", to: "/tasks" },
444
+ /** Toggle the Tasks page visibility */
445
+ enable: get("home_page.tasks.enable", false),
446
+ value: { label: "Tasks", to: `/${UserTasksDir}` },
351
447
  },
352
448
  {
353
- enable: !UserConfig.disable_branding,
449
+ /** Hide the 'Powered by Portosaurus' branding in the menu */
450
+ enable: !get("theme.appearance.disable_branding", false),
354
451
  value: {
355
- label: `Portosaurus v${version}`,
452
+ label: `Portosaurus v${portoVersion}`,
356
453
  className: "_nav-protosaurus-version",
357
- to: "https://github.com/soymadip/portosaurus",
454
+ to: portoRepo,
358
455
  },
359
456
  },
360
457
  ]),
@@ -366,9 +463,23 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
366
463
  maxHeadingLevel: 4,
367
464
  },
368
465
  prism: {
369
- theme: catppuccinLatte,
370
- darkTheme: catppuccinMocha,
371
- additionalLanguages: ["java", "php", "bash"],
466
+ theme: (function () {
467
+ try {
468
+ return require(path.resolve(PortoRoot, "src/theme/config/prism.js"))
469
+ .catppuccinLatte;
470
+ } catch {
471
+ return {};
472
+ }
473
+ })(),
474
+ darkTheme: (function () {
475
+ try {
476
+ return require(path.resolve(PortoRoot, "src/theme/config/prism.js"))
477
+ .catppuccinMocha;
478
+ } catch {
479
+ return {};
480
+ }
481
+ })(),
482
+ additionalLanguages: ["java", "php", "bash", "diff"],
372
483
  },
373
484
  footer: {},
374
485
  },
@@ -412,8 +523,8 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
412
523
  hashed: true,
413
524
  indexDocs: true,
414
525
  docsDir: UserNotesDir,
415
- docsRouteBasePath: "notes",
416
- searchContextByPaths: ["notes", "blog"],
526
+ docsRouteBasePath: UserNotesDir,
527
+ searchContextByPaths: [UserNotesDir, UserBlogDir],
417
528
  highlightSearchTermsOnTargetPage: true,
418
529
  explicitSearchResultPath: true,
419
530
  hideSearchBarWithNoSearchContext: true,
@@ -423,7 +534,11 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
423
534
  "plugin-image-zoom",
424
535
  ],
425
536
 
426
- staticDirectories: StaticDirs,
537
+ staticDirectories: [
538
+ UserStaticDir,
539
+ PortoAssetDir,
540
+ path.resolve(UserRoot, ".docusaurus/portosaurus/favicon"),
541
+ ].filter((d) => fs.existsSync(d)),
427
542
  };
428
543
 
429
544
  return config;