portosaurus 3.0.2 → 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 +6 -12
  11. package/src/cli/schema.mjs +211 -0
  12. package/src/core/buildDocuConfig.mjs +305 -188
  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,206 +1,296 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { createRequire } from "module";
4
-
3
+ import { PortoRoot, Paths, PortoPkg } from "./constants.mjs";
4
+ import {
5
+ loadPkg,
6
+ getGitDate,
7
+ useEnabled,
8
+ getNestedValue,
9
+ } from "../utils/systemUtils.mjs";
5
10
  import {
6
- deepMerge,
7
11
  resolveVars,
8
12
  resolveSiteUrl,
9
13
  resolveBasePath,
10
- getVersion,
11
- useEnabled,
12
14
  createStaticAssetResolver,
15
+ buildHeadTags,
13
16
  } from "../utils/configUtils.mjs";
14
- import { PortoRoot } from "./constants.mjs";
15
-
16
- // ─── Main Config Generator ─────────────────────────────────
17
-
18
- /**
19
- * Creates a Docusaurus config from a user's Portosaurus config.
20
- *
21
- * @param {Object} rawUserConfig - The user's config object (export of config.js).
22
- * May be { usrConf: {...} } or just {...}.
23
- * @param {string} UserRoot - Absolute path to the user's project root.
24
- * @returns {Object} Complete Docusaurus configuration object.
25
- */
26
- export function buildDocuConfig(rawUserConfig, UserRoot) {
27
- // Support both { usrConf: {...} } and direct config objects
28
- const UserConfigRaw = rawUserConfig.usrConf ?? rawUserConfig;
29
-
30
- // Load master template defaults for fallback resolution
31
- const require = createRequire(import.meta.url);
32
- const templateDefaults = require("../template/config.js").usrConf;
33
-
34
- // Static directories — defined early so aliases can reference them
35
- const UserStaticDir = path.resolve(UserRoot, "static");
36
- const PortoAssetDir = path.resolve(PortoRoot, "src/assets");
37
-
38
- // Hydrate the configuration by deep-merging user config over the template defaults.
39
- // This ensures that even if the user deletes nested fields, the structure remains safe.
40
- const merged = deepMerge(templateDefaults, UserConfigRaw);
41
-
42
- // Find Docusaurus root for reference
43
- let docuRoot = "";
44
- try {
45
- docuRoot = path.dirname(require.resolve("@docusaurus/core/package.json"));
46
- } catch {
47
- // If not found (e.g. during some build stages), fallback to node_modules relative to PortoRoot
48
- docuRoot = path.resolve(PortoRoot, "node_modules/@docusaurus/core");
49
- }
50
-
51
- // Resolve {{...}} template variables path prefixes in one pass
52
- const UserConfig = resolveVars(merged, merged, {
53
- siteRoot: UserRoot,
54
- portoRoot: PortoRoot,
55
- docuRoot: docuRoot,
56
- });
57
17
 
58
- // Compute derived values
59
- const siteUrl = resolveSiteUrl(UserConfig.site_url);
60
- const basePath = resolveBasePath(UserConfig.site_path);
61
- const version = getVersion();
62
-
63
- const UserProjName = UserConfig.hero_section.title;
64
-
65
- // Resolve internal paths
66
-
67
- // Prism themes
68
- let catppuccinMocha, catppuccinLatte;
69
- try {
70
- const prism = require(path.resolve(PortoRoot, "src/theme/config/prism.js"));
71
- catppuccinMocha = prism.catppuccinMocha;
72
- catppuccinLatte = prism.catppuccinLatte;
73
- } catch {
74
- catppuccinMocha = {};
75
- catppuccinLatte = {};
76
- }
77
-
78
- // Meta tags
79
- let PortoMetaTags = [];
80
- try {
81
- const meta = require(
82
- path.resolve(PortoRoot, "src/theme/config/metaTags.js"),
83
- );
84
- PortoMetaTags = meta.PortoMetaTags ?? meta.metaTags ?? [];
85
- } catch {
86
- // OK — no meta tags
87
- }
88
-
89
- // Paths for content — these point directly to the user's project
90
- const UserNotesDir = "notes";
91
- const UserBlogDir = "blog";
18
+ export function buildDocuConfig(UserConfigRaw, UserRoot) {
19
+ // Get user Project Version
20
+ const usrProjectVer = loadPkg(UserRoot).version || "0.0.0";
92
21
 
93
- const PortoFaviconCacheDir = path.resolve(
94
- UserRoot,
95
- ".docusaurus/portosaurus/favicon",
96
- );
22
+ const portoVersion = PortoPkg.version || "0.0.0";
23
+ const portoRepo = (PortoPkg.repository?.url || "").replace(/\.git$/, "");
24
+ const lastUpdated = getGitDate(UserRoot);
97
25
 
98
- const StaticDirs = [
99
- UserStaticDir,
100
- PortoAssetDir,
101
- PortoFaviconCacheDir,
102
- ].filter((d) => fs.existsSync(d));
103
-
104
- /**
105
- * Validates a static asset path with fallback.
106
- * - Remote URLs (http/https) pass through as-is.
107
- * - Local paths are checked against UserStaticDir, then PortoAssetDir.
108
- */
26
+ // Static directories
27
+ const UserStaticDir = path.resolve(UserRoot, "static");
28
+ const PortoAssetDir = Paths.assets;
29
+
30
+ // Resolve static asset path with fallback
109
31
  const resolveStaticAsset = createStaticAssetResolver(
32
+ UserRoot,
110
33
  UserStaticDir,
111
34
  PortoAssetDir,
112
35
  );
113
36
 
114
- // Pages
115
- 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");
116
40
 
117
- // Theme overrides
118
- 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
+ });
119
57
 
120
- // Sidebar config
121
- 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);
122
60
 
123
- // CSS
124
- const PortoCustomCss = path.resolve(PortoRoot, "src/theme/css/custom.css");
61
+ // ─────────────────────── Build the Docusaurus config ───────────────────────
125
62
 
126
- // ───────────────────────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";
127
71
 
128
72
  const config = {
129
- projectName: UserConfig.hero_section.title,
130
- title: UserConfig.hero_section.title,
131
- 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
132
86
  favicon: resolveStaticAsset(
133
- UserConfig.favicon,
87
+ get("site.favicon", ""),
134
88
  resolveStaticAsset("favicon/favicon.ico", "img/icon.png"),
135
89
  ),
136
- url: siteUrl,
137
- baseUrl: basePath,
138
90
 
139
- organizationName: UserConfig.hero_section.title,
140
- deploymentBranch: "gh-pages",
141
- onBrokenAnchors: "ignore",
142
- onBrokenLinks: "warn",
91
+ organizationName: siteName,
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"),
143
98
 
99
+ // TODO: research & allow to configure this
144
100
  i18n: {
145
101
  defaultLocale: "en",
146
102
  locales: ["en"],
147
103
  },
148
104
 
149
- 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
+ ]),
150
120
 
151
121
  customFields: {
152
- 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),
153
129
 
154
130
  heroSection: {
155
131
  profilePic: resolveStaticAsset(
156
- UserConfig.hero_section.profile_pic,
132
+ get("home_page.hero.profile_pic", "site.favicon", ""),
157
133
  "img/icon.png",
158
134
  ),
159
- intro: UserConfig.hero_section.intro,
160
- title: UserConfig.hero_section.title,
161
- subtitle: UserConfig.hero_section.subtitle,
162
- profession: UserConfig.hero_section.profession,
163
- description: UserConfig.hero_section.description,
164
- 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),
165
149
  },
166
150
 
167
151
  aboutMe: {
168
- 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 */
169
159
  image: resolveStaticAsset(
170
- 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
+ ),
171
166
  "img/icon.png",
172
167
  ),
173
- description: UserConfig.about_me.description,
174
- skills: UserConfig.about_me.skills,
175
- 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),
186
+ },
187
+
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
+ })),
176
228
  },
177
229
 
178
- projects: UserConfig.project_shelf,
179
- experience: UserConfig.experience,
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
+ },
180
246
 
181
247
  socialLinks: {
182
- enable: UserConfig.social_links.enable,
183
- 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
+ ]),
184
272
  },
185
273
 
186
274
  robotsTxt: {
187
- enable: UserConfig.robots_txt,
188
- rules: [{ disallow: ["/notes/", "/tasks/"] }],
189
- 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", []),
190
281
  },
191
282
 
192
283
  tasksPage: {
193
- enable: UserConfig.tasks_page.enable,
194
- title: UserConfig.tasks_page.title,
195
- description: UserConfig.tasks_page.description,
196
- taskList: UserConfig.tasks_page.tasks,
197
- },
284
+ /** Enable or disable the Tasks/TODO page */
285
+ enable: get("tasks.enable", false),
198
286
 
199
- corsProxyList: [
200
- ...[].concat(UserConfig.cors_proxy || []),
201
- "https://cors-proxy.soymadip.workers.dev/?url=",
202
- "https://api.allorigins.win/raw?url=",
203
- ].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
+ },
204
294
  },
205
295
 
206
296
  presets: [
@@ -210,24 +300,24 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
210
300
  }),
211
301
  {
212
302
  docs: {
213
- routeBasePath: "notes",
303
+ routeBasePath: UserNotesDir,
214
304
  path: UserNotesDir,
215
- sidebarPath,
305
+ sidebarPath: path.resolve(PortoRoot, "src/theme/config/sidebar.js"),
216
306
  admonitions: {
217
- keywords: [
218
- "note",
219
- "tip",
220
- "info",
221
- "warning",
222
- "danger",
223
- "question",
224
- ],
225
307
  extendDefaults: true,
308
+ // keywords: [
309
+ // "note",
310
+ // "tip",
311
+ // "info",
312
+ // "warning",
313
+ // "danger",
314
+ // "question",
315
+ // ],
226
316
  },
227
317
  },
228
318
  blog: {
229
319
  path: UserBlogDir,
230
- feedOptions: UserConfig.rss
320
+ feedOptions: get("site.rss", true)
231
321
  ? { type: ["rss", "atom"], xslt: true }
232
322
  : undefined,
233
323
  showReadingTime: false,
@@ -236,10 +326,10 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
236
326
  onUntruncatedBlogPosts: "warn",
237
327
  },
238
328
  theme: {
239
- customCss: PortoCustomCss,
329
+ customCss: path.resolve(PortoRoot, "src/theme/css/custom.css"),
240
330
  },
241
331
  pages: {
242
- path: PortoPagesDir,
332
+ path: path.resolve(PortoRoot, "src/theme/pages"),
243
333
  },
244
334
  },
245
335
  ],
@@ -251,44 +341,51 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
251
341
  [
252
342
  path.resolve(PortoRoot, "src/plugins/theme.mjs"),
253
343
  {
254
- themeDir: PortoThemeDir,
344
+ themeDir: path.resolve(PortoRoot, "src/theme"),
255
345
  },
256
346
  ],
257
347
  ],
258
348
 
259
349
  markdown: {
260
- mermaid: true,
350
+ mermaid: get("theme.markdown.mermaid", true),
261
351
  hooks: {
262
- onBrokenMarkdownLinks: "warn",
352
+ onBrokenMarkdownLinks: get("theme.markdown.on_broken_links", "throw"),
353
+ onBrokenMarkdownImages: get("theme.markdown.on_broken_images", "throw"),
263
354
  },
264
355
  },
265
356
 
266
357
  themeConfig: {
267
- image: resolveStaticAsset(UserConfig.social_card, "img/social-card.jpeg"),
358
+ image: resolveStaticAsset(
359
+ get("site.social_card", ""),
360
+ "img/social-card.jpeg",
361
+ ),
362
+
268
363
  docs: {
269
364
  sidebar: {
270
- hideable: UserConfig.collapsable_sidebar,
365
+ hideable: get("theme.navigation.collapsable_sidebar", true),
271
366
  },
272
367
  },
368
+
273
369
  imageZoom: {
274
370
  options: {
275
371
  margin: 2,
276
372
  background: "rgba(var(--ifm-background-color-rgb), 0.9)",
277
373
  },
278
374
  },
375
+
279
376
  colorMode: {
280
- defaultMode: UserConfig.dark_mode ? "dark" : "light",
281
- disableSwitch: UserConfig.disable_theme_switch,
377
+ defaultMode: get("theme.appearance.dark_mode", true) ? "dark" : "light",
378
+ disableSwitch: get("theme.appearance.disable_switch", false),
282
379
  },
283
380
  navbar: {
284
- title: UserProjName,
285
- hideOnScroll: UserConfig.hide_navbar_on_scroll,
381
+ title: get("site.title", "Your Portfolio"),
382
+ hideOnScroll: get("theme.navigation.hide_navbar_on_scroll", true),
286
383
  logo: {
287
384
  alt: "Site Logo",
288
385
  src: resolveStaticAsset(
289
- UserConfig.favicon,
386
+ get("site.favicon", ""),
290
387
  resolveStaticAsset(
291
- UserConfig.hero_section.profile_pic,
388
+ get("home_page.hero.profile_pic", ""),
292
389
  "img/icon.png",
293
390
  ),
294
391
  ),
@@ -300,7 +397,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
300
397
  className: "navbar-search-bar",
301
398
  },
302
399
  {
303
- enable: UserConfig.about_me.enable,
400
+ enable: get("home_page.about.enable", true),
304
401
  value: {
305
402
  label: "About Me",
306
403
  to: "/#about",
@@ -309,7 +406,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
309
406
  },
310
407
  },
311
408
  {
312
- enable: UserConfig.project_shelf.enable,
409
+ enable: get("home_page.project_shelf.enable", true),
313
410
  value: {
314
411
  label: "Projects",
315
412
  to: "/#projects",
@@ -318,7 +415,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
318
415
  },
319
416
  },
320
417
  {
321
- enable: UserConfig.experience.enable,
418
+ enable: get("home_page.experience.enable", false),
322
419
  value: {
323
420
  label: "Experience",
324
421
  to: "/#experience",
@@ -327,7 +424,7 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
327
424
  },
328
425
  },
329
426
  {
330
- enable: UserConfig.social_links.enable,
427
+ enable: get("home_page.social.enable", true),
331
428
  value: {
332
429
  label: "Contact",
333
430
  to: "/#contact",
@@ -341,18 +438,20 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
341
438
  position: "right",
342
439
  className: "_navbar-more-items",
343
440
  items: useEnabled([
344
- { label: "Notes", to: "/notes" },
345
- { label: "Blog", to: "/blog" },
441
+ { label: "Notes", to: `/${UserNotesDir}` },
442
+ { label: "Blog", to: `/${UserBlogDir}` },
346
443
  {
347
- enable: UserConfig.tasks_page.enable,
348
- 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}` },
349
447
  },
350
448
  {
351
- enable: !UserConfig.disable_branding,
449
+ /** Hide the 'Powered by Portosaurus' branding in the menu */
450
+ enable: !get("theme.appearance.disable_branding", false),
352
451
  value: {
353
- label: `Portosaurus v${version}`,
452
+ label: `Portosaurus v${portoVersion}`,
354
453
  className: "_nav-protosaurus-version",
355
- to: "https://github.com/soymadip/portosaurus",
454
+ to: portoRepo,
356
455
  },
357
456
  },
358
457
  ]),
@@ -364,9 +463,23 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
364
463
  maxHeadingLevel: 4,
365
464
  },
366
465
  prism: {
367
- theme: catppuccinLatte,
368
- darkTheme: catppuccinMocha,
369
- 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"],
370
483
  },
371
484
  footer: {},
372
485
  },
@@ -410,8 +523,8 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
410
523
  hashed: true,
411
524
  indexDocs: true,
412
525
  docsDir: UserNotesDir,
413
- docsRouteBasePath: "notes",
414
- searchContextByPaths: ["notes", "blog"],
526
+ docsRouteBasePath: UserNotesDir,
527
+ searchContextByPaths: [UserNotesDir, UserBlogDir],
415
528
  highlightSearchTermsOnTargetPage: true,
416
529
  explicitSearchResultPath: true,
417
530
  hideSearchBarWithNoSearchContext: true,
@@ -421,7 +534,11 @@ export function buildDocuConfig(rawUserConfig, UserRoot) {
421
534
  "plugin-image-zoom",
422
535
  ],
423
536
 
424
- staticDirectories: StaticDirs,
537
+ staticDirectories: [
538
+ UserStaticDir,
539
+ PortoAssetDir,
540
+ path.resolve(UserRoot, ".docusaurus/portosaurus/favicon"),
541
+ ].filter((d) => fs.existsSync(d)),
425
542
  };
426
543
 
427
544
  return config;