portosaurus 3.0.2 → 4.0.1
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 +26 -126
- package/bin/portosaurus.mjs +8 -0
- package/package.json +6 -3
- package/src/assets/img/icon.png +0 -0
- package/src/assets/img/{icon.svg → svg/icon.svg} +35 -37
- package/src/assets/img/svg/project-blank.svg +140 -0
- package/src/assets/sample-resume.pdf +0 -0
- package/src/cli/build.mjs +2 -5
- package/src/cli/dev.mjs +27 -5
- package/src/cli/init.mjs +6 -12
- package/src/cli/schema.mjs +211 -0
- package/src/core/buildDocuConfig.mjs +306 -189
- package/src/core/constants.mjs +7 -1
- package/src/template/config.yml +150 -0
- package/src/template/notes/welcome.mdx +6 -0
- package/src/template/package.json +3 -3
- package/src/theme/MDXComponents.js +0 -1
- package/src/theme/components/AboutSection/index.js +39 -17
- package/src/theme/components/AboutSection/styles.module.css +151 -344
- package/src/theme/components/ContactSection/index.js +29 -14
- package/src/theme/components/ContactSection/styles.module.css +19 -8
- package/src/theme/components/ExperienceSection/index.js +19 -5
- package/src/theme/components/HeroSection/index.js +11 -4
- package/src/theme/components/HeroSection/styles.module.css +17 -16
- package/src/theme/components/NavArrow/index.js +114 -0
- package/src/theme/components/NavArrow/styles.module.css +107 -0
- package/src/theme/components/NoteIndex/index.js +66 -95
- package/src/theme/components/NoteIndex/styles.module.css +85 -89
- package/src/theme/components/Preview/components/FeedbackStates.js +3 -1
- package/src/theme/components/Preview/components/PreviewContent.js +91 -0
- package/src/theme/components/Preview/components/PreviewHeader.js +41 -33
- package/src/theme/components/Preview/components/Triggers/Pv.js +129 -72
- package/src/theme/components/Preview/components/ViewerWindow.js +198 -234
- package/src/theme/components/Preview/hooks/useAdaptiveSizing.js +115 -0
- package/src/theme/components/Preview/hooks/useDeepLinkHash.js +18 -23
- package/src/theme/components/Preview/hooks/useDockLayout.js +48 -8
- package/src/theme/components/Preview/hooks/useTouchZoom.js +118 -0
- package/src/theme/components/Preview/renderers/CodeRenderer.js +64 -25
- package/src/theme/components/Preview/state/index.js +70 -17
- package/src/theme/components/Preview/styles.module.css +181 -45
- package/src/theme/components/Preview/utils/index.js +11 -10
- package/src/theme/components/ProjectsSection/index.js +145 -148
- package/src/theme/components/ProjectsSection/styles.module.css +178 -112
- package/src/theme/components/SocialLinks/index.js +9 -7
- package/src/theme/components/Tooltip/index.js +31 -20
- package/src/theme/components/Tooltip/styles.module.css +101 -38
- package/src/theme/config/iconMappings.js +2 -0
- package/src/theme/css/custom.css +72 -0
- package/src/theme/hooks/useScrollReveal.js +30 -0
- package/src/theme/pages/index.js +7 -27
- package/src/theme/pages/notes.js +2 -2
- package/src/theme/pages/tasks.js +12 -11
- package/src/utils/cliUtils.mjs +23 -51
- package/src/utils/configUtils.mjs +95 -84
- package/src/utils/systemUtils.mjs +171 -0
- package/src/template/config.js +0 -68
- package/src/theme/components/ScrollToTop/index.js +0 -95
- package/src/theme/components/ScrollToTop/styles.module.css +0 -97
- package/src/theme/config/metaTags.js +0 -21
- /package/src/template/{.nojekyll → static/.nojekyll} +0 -0
|
@@ -8,98 +8,161 @@
|
|
|
8
8
|
|
|
9
9
|
.hasUnderline {
|
|
10
10
|
cursor: help;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
text-decoration: none;
|
|
12
|
+
background-image: radial-gradient(
|
|
13
|
+
circle,
|
|
14
|
+
color-mix(in srgb, var(--ifm-color-primary), transparent 40%) 1px,
|
|
15
|
+
transparent 1.2px
|
|
16
|
+
);
|
|
17
|
+
background-position: bottom;
|
|
18
|
+
background-size: 6px 2.5px;
|
|
19
|
+
background-repeat: space no-repeat;
|
|
20
|
+
padding-bottom: 1px;
|
|
21
|
+
transition: background-image 0.2s ease;
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
.hasUnderline:hover {
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
background-image: radial-gradient(
|
|
26
|
+
circle,
|
|
27
|
+
color-mix(in srgb, var(--ifm-color-primary-darker), transparent 10%) 1.2px,
|
|
28
|
+
transparent 1.5px
|
|
29
|
+
);
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
.tooltip {
|
|
21
33
|
position: fixed;
|
|
22
34
|
background-color: var(--tooltip-color, var(--ifm-background-surface-color));
|
|
23
35
|
color: var(--tooltip-text-color, var(--ifm-font-color-base));
|
|
24
|
-
padding:
|
|
25
|
-
border-radius:
|
|
26
|
-
font-size: 0.
|
|
27
|
-
font-weight:
|
|
36
|
+
padding: 8px 12px;
|
|
37
|
+
border-radius: var(--ifm-global-radius);
|
|
38
|
+
font-size: 0.85rem;
|
|
39
|
+
font-weight: 450;
|
|
40
|
+
text-align: center;
|
|
41
|
+
line-height: 1.4;
|
|
28
42
|
white-space: normal;
|
|
29
43
|
word-break: break-word;
|
|
30
|
-
|
|
44
|
+
overflow-wrap: anywhere;
|
|
45
|
+
max-width: min(240px, 90vw);
|
|
31
46
|
width: max-content;
|
|
32
47
|
z-index: 9999;
|
|
33
48
|
opacity: 0;
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
filter: var(
|
|
50
|
+
--tooltip-shadow,
|
|
51
|
+
drop-shadow(0 8px 24px rgba(var(--ifm-color-emphasis-900-rgb), 0.3))
|
|
52
|
+
);
|
|
53
|
+
border: 1px solid var(--ifm-color-emphasis-300);
|
|
36
54
|
pointer-events: none;
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
height: 8px;
|
|
43
|
-
background-color: inherit;
|
|
57
|
+
.top {
|
|
58
|
+
transform: translateX(-50%) translateY(-100%);
|
|
59
|
+
animation: popUp 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
|
44
60
|
}
|
|
45
61
|
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
.bottom {
|
|
63
|
+
transform: translateX(-50%);
|
|
64
|
+
animation: popDown 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
|
65
|
+
}
|
|
48
66
|
|
|
49
|
-
.
|
|
50
|
-
transform: translateX(-
|
|
67
|
+
.left {
|
|
68
|
+
transform: translateX(-100%) translateY(-50%);
|
|
69
|
+
animation: popLeft 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.right {
|
|
73
|
+
transform: translateY(-50%);
|
|
74
|
+
animation: popRight 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.arrow {
|
|
78
|
+
position: absolute;
|
|
79
|
+
width: 10px;
|
|
80
|
+
height: 10px;
|
|
81
|
+
background-color: inherit;
|
|
51
82
|
}
|
|
52
83
|
|
|
53
84
|
.top .arrow {
|
|
54
85
|
top: 100%;
|
|
55
86
|
left: 50%;
|
|
56
87
|
transform: translateX(-50%) rotate(45deg);
|
|
57
|
-
margin-top: -
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.bottom {
|
|
61
|
-
transform: translateX(-50%);
|
|
88
|
+
margin-top: -5.5px;
|
|
89
|
+
border-right: 1px solid var(--ifm-color-emphasis-300);
|
|
90
|
+
border-bottom: 1px solid var(--ifm-color-emphasis-300);
|
|
62
91
|
}
|
|
63
92
|
|
|
64
93
|
.bottom .arrow {
|
|
65
94
|
bottom: 100%;
|
|
66
95
|
left: 50%;
|
|
67
96
|
transform: translateX(-50%) rotate(45deg);
|
|
68
|
-
margin-bottom: -
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.left {
|
|
72
|
-
transform: translateX(-100%) translateY(-50%);
|
|
97
|
+
margin-bottom: -5.5px;
|
|
98
|
+
border-left: 1px solid var(--ifm-color-emphasis-300);
|
|
99
|
+
border-top: 1px solid var(--ifm-color-emphasis-300);
|
|
73
100
|
}
|
|
74
101
|
|
|
75
102
|
.left .arrow {
|
|
76
|
-
right: -
|
|
103
|
+
right: -5.5px;
|
|
77
104
|
top: 50%;
|
|
78
105
|
transform: translateY(-50%) rotate(45deg);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
.right {
|
|
82
|
-
transform: translateY(-50%);
|
|
106
|
+
border-right: 1px solid var(--ifm-color-emphasis-300);
|
|
107
|
+
border-top: 1px solid var(--ifm-color-emphasis-300);
|
|
83
108
|
}
|
|
84
109
|
|
|
85
110
|
.right .arrow {
|
|
86
|
-
left: -
|
|
111
|
+
left: -5.5px;
|
|
87
112
|
top: 50%;
|
|
88
113
|
transform: translateY(-50%) rotate(45deg);
|
|
114
|
+
border-left: 1px solid var(--ifm-color-emphasis-300);
|
|
115
|
+
border-bottom: 1px solid var(--ifm-color-emphasis-300);
|
|
89
116
|
}
|
|
90
117
|
|
|
91
|
-
@keyframes
|
|
118
|
+
@keyframes popUp {
|
|
92
119
|
from {
|
|
93
120
|
opacity: 0;
|
|
121
|
+
transform: translateX(-50%) translateY(-95%) scale(0.9);
|
|
94
122
|
}
|
|
95
123
|
to {
|
|
96
124
|
opacity: 1;
|
|
125
|
+
transform: translateX(-50%) translateY(-100%) scale(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@keyframes popDown {
|
|
130
|
+
from {
|
|
131
|
+
opacity: 0;
|
|
132
|
+
transform: translateX(-50%) translateY(-5%) scale(0.9);
|
|
133
|
+
}
|
|
134
|
+
to {
|
|
135
|
+
opacity: 1;
|
|
136
|
+
transform: translateX(-50%) translateY(0) scale(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@keyframes popLeft {
|
|
141
|
+
from {
|
|
142
|
+
opacity: 0;
|
|
143
|
+
transform: translateX(-95%) translateY(-50%) scale(0.9);
|
|
144
|
+
}
|
|
145
|
+
to {
|
|
146
|
+
opacity: 1;
|
|
147
|
+
transform: translateX(-100%) translateY(-50%) scale(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@keyframes popRight {
|
|
152
|
+
from {
|
|
153
|
+
opacity: 0;
|
|
154
|
+
transform: translateX(5%) translateY(-50%) scale(0.9);
|
|
155
|
+
}
|
|
156
|
+
to {
|
|
157
|
+
opacity: 1;
|
|
158
|
+
transform: translateX(0) translateY(-50%) scale(1);
|
|
97
159
|
}
|
|
98
160
|
}
|
|
99
161
|
|
|
100
162
|
/* Accessibility */
|
|
101
163
|
@media (prefers-reduced-motion: reduce) {
|
|
102
164
|
.tooltip {
|
|
103
|
-
animation: none;
|
|
165
|
+
animation: none !important;
|
|
166
|
+
opacity: 1;
|
|
104
167
|
}
|
|
105
168
|
}
|
|
@@ -106,6 +106,7 @@ import {
|
|
|
106
106
|
SiElasticsearch,
|
|
107
107
|
SiGraphql,
|
|
108
108
|
SiGoogledrive,
|
|
109
|
+
SiLua,
|
|
109
110
|
} from "react-icons/si";
|
|
110
111
|
|
|
111
112
|
import {
|
|
@@ -204,6 +205,7 @@ export const iconMap = {
|
|
|
204
205
|
php: { icon: FaPhp, color: "#777BB4" },
|
|
205
206
|
html: { icon: FaHtml5, color: "#E34F26" },
|
|
206
207
|
css: { icon: FaCss3Alt, color: "#1572B6" },
|
|
208
|
+
lua: { icon: SiLua, color: "#000080" },
|
|
207
209
|
sql: { icon: SiMysql, color: "#4479A1" },
|
|
208
210
|
postgresql: { icon: BiLogoPostgresql, color: "#336791" },
|
|
209
211
|
postgres: { icon: BiLogoPostgresql, color: "#336791" },
|
package/src/theme/css/custom.css
CHANGED
|
@@ -179,3 +179,75 @@ mark {
|
|
|
179
179
|
justify-content: center;
|
|
180
180
|
align-items: center;
|
|
181
181
|
}
|
|
182
|
+
|
|
183
|
+
/* === Global Entrance Animations === */
|
|
184
|
+
|
|
185
|
+
@keyframes fadeIn {
|
|
186
|
+
from {
|
|
187
|
+
opacity: 0;
|
|
188
|
+
}
|
|
189
|
+
to {
|
|
190
|
+
opacity: 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@keyframes slideUp {
|
|
195
|
+
from {
|
|
196
|
+
opacity: 0;
|
|
197
|
+
transform: translateY(30px);
|
|
198
|
+
}
|
|
199
|
+
to {
|
|
200
|
+
opacity: 1;
|
|
201
|
+
transform: translateY(0);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@keyframes slideInLeft {
|
|
206
|
+
from {
|
|
207
|
+
opacity: 0;
|
|
208
|
+
transform: translateX(-40px);
|
|
209
|
+
}
|
|
210
|
+
to {
|
|
211
|
+
opacity: 1;
|
|
212
|
+
transform: translateX(0);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@keyframes slideInRight {
|
|
217
|
+
from {
|
|
218
|
+
opacity: 0;
|
|
219
|
+
transform: translateX(40px);
|
|
220
|
+
}
|
|
221
|
+
to {
|
|
222
|
+
opacity: 1;
|
|
223
|
+
transform: translateX(0);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@keyframes scaleIn {
|
|
228
|
+
from {
|
|
229
|
+
opacity: 0;
|
|
230
|
+
transform: scale(0.95);
|
|
231
|
+
}
|
|
232
|
+
to {
|
|
233
|
+
opacity: 1;
|
|
234
|
+
transform: scale(1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@keyframes skillAppear {
|
|
239
|
+
to {
|
|
240
|
+
opacity: 1;
|
|
241
|
+
transform: translateY(0);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Base reveal state */
|
|
246
|
+
.reveal-on-scroll {
|
|
247
|
+
opacity: 0;
|
|
248
|
+
transition: opacity 0.3s ease;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.reveal-on-scroll.isVisible {
|
|
252
|
+
opacity: 1;
|
|
253
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A reusable hook to detect when an element enters the viewport.
|
|
5
|
+
* @param {Object} options IntersectionObserver options
|
|
6
|
+
* @returns {Array} [ref, isVisible]
|
|
7
|
+
*/
|
|
8
|
+
export default function useScrollReveal(options = { threshold: 0.05 }) {
|
|
9
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
10
|
+
const elementRef = useRef(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
14
|
+
setIsVisible(entry.isIntersecting);
|
|
15
|
+
}, options);
|
|
16
|
+
|
|
17
|
+
const currentElement = elementRef.current;
|
|
18
|
+
if (currentElement) {
|
|
19
|
+
observer.observe(currentElement);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
if (currentElement) {
|
|
24
|
+
observer.unobserve(currentElement);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}, [options]);
|
|
28
|
+
|
|
29
|
+
return [elementRef, isVisible];
|
|
30
|
+
}
|
package/src/theme/pages/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import AboutSection from "../components/AboutSection/index.js";
|
|
|
8
8
|
import ProjectsSection from "../components/ProjectsSection/index.js";
|
|
9
9
|
import ContactSection from "../components/ContactSection/index.js";
|
|
10
10
|
import ExperienceSection from "../components/ExperienceSection/index.js";
|
|
11
|
-
import
|
|
11
|
+
import NavArrow from "../components/NavArrow/index.js";
|
|
12
12
|
|
|
13
13
|
export default function Home() {
|
|
14
14
|
const { siteConfig } = useDocusaurusContext();
|
|
@@ -43,39 +43,19 @@ export default function Home() {
|
|
|
43
43
|
<HeroSection id="me" />
|
|
44
44
|
|
|
45
45
|
{/* About Section */}
|
|
46
|
-
|
|
47
|
-
<AboutSection id="about" title="About Me" />
|
|
48
|
-
)}
|
|
46
|
+
<AboutSection id="about" />
|
|
49
47
|
|
|
50
48
|
{/* Projects Section */}
|
|
51
|
-
|
|
52
|
-
<ProjectsSection
|
|
53
|
-
id="projects"
|
|
54
|
-
title="My Projects"
|
|
55
|
-
subtitle="A collection of all my works, with featured projects highlighted"
|
|
56
|
-
/>
|
|
57
|
-
)}
|
|
49
|
+
<ProjectsSection id="projects" />
|
|
58
50
|
|
|
59
51
|
{/* Experience Section */}
|
|
60
|
-
|
|
61
|
-
<ExperienceSection
|
|
62
|
-
id="experience"
|
|
63
|
-
title="Experience"
|
|
64
|
-
subtitle="My professional journey and work experience"
|
|
65
|
-
/>
|
|
66
|
-
)}
|
|
52
|
+
<ExperienceSection id="experience" />
|
|
67
53
|
|
|
68
54
|
{/* Contact Section */}
|
|
69
|
-
|
|
70
|
-
<ContactSection
|
|
71
|
-
id="contact"
|
|
72
|
-
title="Get In Touch"
|
|
73
|
-
subtitle="Feel free to reach out for collaborations, questions, or just to say hello!"
|
|
74
|
-
/>
|
|
75
|
-
)}
|
|
55
|
+
<ContactSection id="contact" />
|
|
76
56
|
|
|
77
|
-
{/*
|
|
78
|
-
<
|
|
57
|
+
{/* Smart Navigation Arrow */}
|
|
58
|
+
<NavArrow />
|
|
79
59
|
</main>
|
|
80
60
|
</Layout>
|
|
81
61
|
);
|
package/src/theme/pages/notes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Layout from "@theme/Layout";
|
|
2
2
|
import NoteCards from "../components/NoteIndex/index.js";
|
|
3
3
|
import { usePluginData } from "@docusaurus/useGlobalData";
|
|
4
|
-
import
|
|
4
|
+
import NavArrow from "../components/NavArrow/index.js";
|
|
5
5
|
import HashNavigation from "../utils/HashNavigation.js";
|
|
6
6
|
|
|
7
7
|
const style = {
|
|
@@ -70,7 +70,7 @@ export default function Notes() {
|
|
|
70
70
|
</p>
|
|
71
71
|
</header>
|
|
72
72
|
<NoteCards />
|
|
73
|
-
<
|
|
73
|
+
<NavArrow />
|
|
74
74
|
<HashNavigation
|
|
75
75
|
elementPrefix="note-"
|
|
76
76
|
elementSelector=".note-card"
|
package/src/theme/pages/tasks.js
CHANGED
|
@@ -97,8 +97,8 @@ function TaskList({ filterStatus, taskList }) {
|
|
|
97
97
|
|
|
98
98
|
<div className="task-cell task-cell-title">
|
|
99
99
|
<div className="task-title">{task.title}</div>
|
|
100
|
-
{task.
|
|
101
|
-
<div className="task-description">{task.
|
|
100
|
+
{task.desc && (
|
|
101
|
+
<div className="task-description">{task.desc}</div>
|
|
102
102
|
)}
|
|
103
103
|
</div>
|
|
104
104
|
|
|
@@ -256,8 +256,8 @@ export default function TasksPage() {
|
|
|
256
256
|
const { customFields } = siteConfig || {};
|
|
257
257
|
|
|
258
258
|
const tasksPage = customFields?.tasksPage;
|
|
259
|
-
const
|
|
260
|
-
const
|
|
259
|
+
const heading = tasksPage.heading || "Tasks";
|
|
260
|
+
const subheading = tasksPage.subheading || "Roadmap & Goals";
|
|
261
261
|
const taskList =
|
|
262
262
|
tasksPage.enable && tasksPage.taskList ? tasksPage.taskList : [];
|
|
263
263
|
|
|
@@ -276,7 +276,7 @@ export default function TasksPage() {
|
|
|
276
276
|
</div>
|
|
277
277
|
<h2 className="disabled-title">Tasks are currently disabled</h2>
|
|
278
278
|
<p className="disabled-help">
|
|
279
|
-
To enable tasks, set <code>
|
|
279
|
+
To enable tasks, set <code>tasks.enable</code> to{" "}
|
|
280
280
|
<code>true</code>
|
|
281
281
|
</p>
|
|
282
282
|
</div>
|
|
@@ -287,17 +287,18 @@ export default function TasksPage() {
|
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
return (
|
|
290
|
-
<Layout title={
|
|
290
|
+
<Layout title={heading} description={subheading}>
|
|
291
291
|
<Head>
|
|
292
|
-
<meta property="og:title" content={
|
|
293
|
-
<meta property="og:description" content={
|
|
294
|
-
<meta name="twitter:title" content={
|
|
295
|
-
<meta name="twitter:description" content={
|
|
292
|
+
<meta property="og:title" content={heading} />
|
|
293
|
+
<meta property="og:description" content={subheading} />
|
|
294
|
+
<meta name="twitter:title" content={heading} />
|
|
295
|
+
<meta name="twitter:description" content={subheading} />
|
|
296
296
|
</Head>
|
|
297
297
|
|
|
298
298
|
<div className="tasks-container">
|
|
299
299
|
<div className="tasks-header">
|
|
300
|
-
<h1 className="tasks-heading">{
|
|
300
|
+
<h1 className="tasks-heading">{heading}</h1>
|
|
301
|
+
{subheading && <p className="tasks-subheading">{subheading}</p>}
|
|
301
302
|
</div>
|
|
302
303
|
|
|
303
304
|
<div className="tasks-content">
|
package/src/utils/cliUtils.mjs
CHANGED
|
@@ -12,17 +12,32 @@ export function writePortoConfigShim(UserRoot) {
|
|
|
12
12
|
const dotDir = path.join(UserRoot, ".docusaurus", "portosaurus");
|
|
13
13
|
fs.mkdirSync(dotDir, { recursive: true });
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const configYaml = ["config.yaml", "config.yml"].find((f) =>
|
|
16
|
+
fs.existsSync(path.join(UserRoot, f)),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (!configYaml) {
|
|
20
|
+
logger.error("No config.yaml or config.yml found in project root.");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const configYamlAbsolute = path
|
|
25
|
+
.join(UserRoot, configYaml)
|
|
26
|
+
.replace(/\\/g, "/");
|
|
16
27
|
const createDocuConfAbsolute = path
|
|
17
28
|
.resolve(PortoRoot, "src/core/buildDocuConfig.mjs")
|
|
18
29
|
.replace(/\\/g, "/");
|
|
19
30
|
|
|
20
31
|
const shimContent = `// Auto-generated by Portosaurus CLI — do not edit
|
|
32
|
+
import fs from "fs";
|
|
33
|
+
import yaml from "js-yaml";
|
|
34
|
+
|
|
21
35
|
/** @type {import('@docusaurus/types').Config} */
|
|
22
36
|
export default async function getConfig() {
|
|
23
37
|
const { buildDocuConfig } = await import("file://${createDocuConfAbsolute}");
|
|
24
|
-
const
|
|
25
|
-
|
|
38
|
+
const yamlContent = fs.readFileSync("${configYamlAbsolute}", "utf8");
|
|
39
|
+
const rawConf = yaml.load(yamlContent);
|
|
40
|
+
return buildDocuConfig(rawConf, "${UserRoot.replace(/\\/g, "/")}");
|
|
26
41
|
}
|
|
27
42
|
`;
|
|
28
43
|
|
|
@@ -37,11 +52,13 @@ export default async function getConfig() {
|
|
|
37
52
|
* Validate that the current directory is a Portosaurus project.
|
|
38
53
|
*/
|
|
39
54
|
export function validateProject(UserRoot) {
|
|
40
|
-
const configPath =
|
|
41
|
-
|
|
55
|
+
const configPath = ["config.yaml", "config.yml"].find((f) =>
|
|
56
|
+
fs.existsSync(path.join(UserRoot, f)),
|
|
57
|
+
);
|
|
58
|
+
if (!configPath) {
|
|
42
59
|
logger.log("");
|
|
43
60
|
logger.error(
|
|
44
|
-
"config.
|
|
61
|
+
"config.yml/config.yaml not found. Are you in a Portosaurus project directory?",
|
|
45
62
|
);
|
|
46
63
|
process.exit(1);
|
|
47
64
|
}
|
|
@@ -99,48 +116,3 @@ export function runDocusaurus(command, extraArgs, UserRoot, configPath) {
|
|
|
99
116
|
});
|
|
100
117
|
});
|
|
101
118
|
}
|
|
102
|
-
|
|
103
|
-
// Recursively copy a directory and apply text replacements.
|
|
104
|
-
export function mirrorSync(src, dest, replacements = {}, ignores = []) {
|
|
105
|
-
const ignoreSet = new Set(ignores);
|
|
106
|
-
const textExtensions = new Set([
|
|
107
|
-
".js",
|
|
108
|
-
".mjs",
|
|
109
|
-
".json",
|
|
110
|
-
".md",
|
|
111
|
-
".mdx",
|
|
112
|
-
".yml",
|
|
113
|
-
".yaml",
|
|
114
|
-
".css",
|
|
115
|
-
".html",
|
|
116
|
-
".txt",
|
|
117
|
-
]);
|
|
118
|
-
|
|
119
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
120
|
-
|
|
121
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
122
|
-
if (ignoreSet.has(entry.name)) continue;
|
|
123
|
-
|
|
124
|
-
const srcPath = path.join(src, entry.name);
|
|
125
|
-
const destPath = path.join(dest, entry.name);
|
|
126
|
-
|
|
127
|
-
if (entry.isDirectory()) {
|
|
128
|
-
mirrorSync(srcPath, destPath, replacements, ignores);
|
|
129
|
-
} else {
|
|
130
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
131
|
-
|
|
132
|
-
// If replacements exist and it's a known text file, process it
|
|
133
|
-
if (Object.keys(replacements).length > 0 && textExtensions.has(ext)) {
|
|
134
|
-
let content = fs.readFileSync(srcPath, "utf8");
|
|
135
|
-
for (const [key, value] of Object.entries(replacements)) {
|
|
136
|
-
// Replace all occurrences of {{key}}
|
|
137
|
-
const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
138
|
-
content = content.replace(regex, value);
|
|
139
|
-
}
|
|
140
|
-
fs.writeFileSync(destPath, content);
|
|
141
|
-
} else {
|
|
142
|
-
fs.copyFileSync(srcPath, destPath); // Safe copy for binaries (images, etc) or when no replacements needed
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|