@portosaur/theme 0.1.5 → 0.2.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 (31) hide show
  1. package/package.json +10 -3
  2. package/src/plugins/theme.mjs +2 -0
  3. package/theme/DocCategoryGeneratedIndexPage/index.jsx +4 -10
  4. package/theme/MDXComponents.jsx +1 -1
  5. package/theme/Root.jsx +1 -1
  6. package/theme/components/AboutSection/index.jsx +89 -249
  7. package/theme/components/ContactSection/index.jsx +72 -153
  8. package/theme/components/ExperienceSection/index.jsx +35 -106
  9. package/theme/components/HeroSection/index.jsx +64 -186
  10. package/theme/components/NavArrow/index.jsx +38 -55
  11. package/theme/components/NoteIndex/index.jsx +50 -116
  12. package/theme/components/Preview/components/FeedbackStates.jsx +45 -190
  13. package/theme/components/Preview/components/FileTabs.jsx +17 -24
  14. package/theme/components/Preview/components/PreviewContent.jsx +37 -62
  15. package/theme/components/Preview/components/PreviewHeader.jsx +146 -380
  16. package/theme/components/Preview/components/Triggers/Pv.jsx +50 -78
  17. package/theme/components/Preview/components/Triggers/SrcPv.jsx +16 -47
  18. package/theme/components/Preview/components/Triggers/index.jsx +2 -2
  19. package/theme/components/Preview/components/ViewerWindow.jsx +160 -268
  20. package/theme/components/Preview/index.jsx +3 -3
  21. package/theme/components/Preview/renderers/CodeRenderer.jsx +81 -109
  22. package/theme/components/Preview/renderers/ImageRenderer.jsx +30 -67
  23. package/theme/components/Preview/renderers/PdfRenderer.jsx +31 -52
  24. package/theme/components/Preview/renderers/WebRenderer.jsx +18 -32
  25. package/theme/components/Preview/state/index.jsx +46 -30
  26. package/theme/components/ProjectsSection/index.jsx +278 -573
  27. package/theme/components/SocialLinks/index.jsx +43 -55
  28. package/theme/components/Tooltip/index.jsx +28 -39
  29. package/theme/pages/index.jsx +23 -87
  30. package/theme/pages/notes.jsx +26 -104
  31. package/theme/pages/tasks.jsx +220 -903
@@ -2,11 +2,13 @@ import React, { useState, useEffect, useRef } from "react";
2
2
  import { FaChevronDown, FaChevronUp } from "react-icons/fa";
3
3
  import Tooltip from "../Tooltip";
4
4
  import styles from "./styles.module.css";
5
+
5
6
  export default function NavArrow() {
6
7
  const [direction, setDirection] = useState("down");
7
8
  const [isVisible, setIsVisible] = useState(false);
8
9
  const [isScrolling, setIsScrolling] = useState(false);
9
10
  const scrollTimeoutRef = useRef(null);
11
+
10
12
  const getSections = () => {
11
13
  const main = document.querySelector("main");
12
14
  if (!main) return [];
@@ -14,29 +16,36 @@ export default function NavArrow() {
14
16
  .map((el) => el.id)
15
17
  .filter((id) => id !== "nav-arrow");
16
18
  };
19
+
17
20
  useEffect(() => {
18
21
  const handleScroll = () => {
19
22
  const scrollTop =
20
23
  window.pageYOffset || document.documentElement.scrollTop;
21
24
  const windowHeight = window.innerHeight;
22
25
  const fullHeight = document.documentElement.scrollHeight;
26
+
23
27
  setIsVisible(scrollTop > 100);
28
+
24
29
  if (scrollTop + windowHeight >= fullHeight - 50) {
25
30
  setDirection("up");
26
31
  } else {
27
32
  setDirection("down");
28
33
  }
34
+
29
35
  setIsScrolling(true);
30
36
  if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
31
37
  scrollTimeoutRef.current = setTimeout(() => setIsScrolling(false), 800);
32
38
  };
39
+
33
40
  window.addEventListener("scroll", handleScroll, { passive: true });
34
41
  handleScroll();
42
+
35
43
  return () => {
36
44
  window.removeEventListener("scroll", handleScroll);
37
45
  if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
38
46
  };
39
47
  }, []);
48
+
40
49
  const handleClick = () => {
41
50
  if (direction === "up") {
42
51
  window.scrollTo({ top: 0, behavior: "smooth" });
@@ -45,6 +54,7 @@ export default function NavArrow() {
45
54
  const windowTop =
46
55
  window.pageYOffset || document.documentElement.scrollTop;
47
56
  let nextSectionId = null;
57
+
48
58
  for (const id of sections) {
49
59
  const element = document.getElementById(id);
50
60
  if (element) {
@@ -56,9 +66,11 @@ export default function NavArrow() {
56
66
  }
57
67
  }
58
68
  }
69
+
59
70
  if (nextSectionId) {
60
- const element = document.getElementById(nextSectionId);
61
- element.scrollIntoView({ behavior: "smooth" });
71
+ document
72
+ .getElementById(nextSectionId)
73
+ .scrollIntoView({ behavior: "smooth" });
62
74
  } else {
63
75
  window.scrollTo({
64
76
  top: document.documentElement.scrollHeight,
@@ -67,58 +79,29 @@ export default function NavArrow() {
67
79
  }
68
80
  }
69
81
  };
70
- return jsxDEV_7x81h0kn(
71
- "button",
72
- {
73
- className: `${styles.navArrow} ${isVisible ? styles.visible : ""} ${isScrolling ? styles.scrolling : ""}`,
74
- onClick: handleClick,
75
- "aria-label":
76
- direction === "down" ? "Scroll to next section" : "Scroll to top",
77
- children: jsxDEV_7x81h0kn(
78
- Tooltip,
79
- {
80
- msg: direction === "down" ? "Next Section" : "Back to Top",
81
- position: "top",
82
- gap: 25,
83
- underline: false,
84
- children: jsxDEV_7x81h0kn(
85
- "div",
86
- {
87
- className: `${styles.iconWrapper} ${styles[direction]}`,
88
- children:
89
- direction === "down"
90
- ? jsxDEV_7x81h0kn(
91
- FaChevronDown,
92
- { className: styles.chevron },
93
- undefined,
94
- false,
95
- undefined,
96
- this,
97
- )
98
- : jsxDEV_7x81h0kn(
99
- FaChevronUp,
100
- { className: styles.chevron },
101
- undefined,
102
- false,
103
- undefined,
104
- this,
105
- ),
106
- },
107
- undefined,
108
- false,
109
- undefined,
110
- this,
111
- ),
112
- },
113
- undefined,
114
- false,
115
- undefined,
116
- this,
117
- ),
118
- },
119
- undefined,
120
- false,
121
- undefined,
122
- this,
82
+
83
+ return (
84
+ <button
85
+ className={`${styles.navArrow} ${isVisible ? styles.visible : ""} ${isScrolling ? styles.scrolling : ""}`}
86
+ onClick={handleClick}
87
+ aria-label={
88
+ direction === "down" ? "Scroll to next section" : "Scroll to top"
89
+ }
90
+ >
91
+ <Tooltip
92
+ msg={direction === "down" ? "Next Section" : "Back to Top"}
93
+ position="top"
94
+ gap={25}
95
+ underline={false}
96
+ >
97
+ <div className={`${styles.iconWrapper} ${styles[direction]}`}>
98
+ {direction === "down" ? (
99
+ <FaChevronDown className={styles.chevron} />
100
+ ) : (
101
+ <FaChevronUp className={styles.chevron} />
102
+ )}
103
+ </div>
104
+ </Tooltip>
105
+ </button>
123
106
  );
124
107
  }
@@ -2,12 +2,14 @@ import useBaseUrl from "@docusaurus/useBaseUrl";
2
2
  import { usePluginData } from "@docusaurus/useGlobalData";
3
3
  import Link from "@docusaurus/Link";
4
4
  import { FaBook, FaChevronRight } from "react-icons/fa";
5
- import Tooltip from "../Tooltip/index.js";
6
- import { iconMap } from "../../config/iconMappings.js";
5
+ import Tooltip from "../Tooltip/index.jsx";
6
+ import { iconMap } from "../../config/iconMappings.jsx";
7
7
  import DocCardList from "@theme/DocCardList";
8
8
  import styles from "./styles.module.css";
9
+
9
10
  function useNotes() {
10
11
  const context = require.context(`@site/notes`, true, /index\.mdx?$|\.mdx?$/);
12
+
11
13
  return context
12
14
  .keys()
13
15
  .filter((path) => {
@@ -35,6 +37,7 @@ function useNotes() {
35
37
  .replace(/ /g, "")
36
38
  .replace(/[\s-]/g, "")
37
39
  : slug.toLowerCase() || title.toLowerCase();
40
+
38
41
  return {
39
42
  title,
40
43
  language,
@@ -45,138 +48,69 @@ function useNotes() {
45
48
  })
46
49
  .sort((a, b) => a.position - b.position);
47
50
  }
51
+
48
52
  function NoteCard({ title, language, slug, desc, index, docsBasePath }) {
49
53
  const noteUrl = useBaseUrl(`${docsBasePath}/${slug}`);
50
54
  const { icon: Icon = FaBook, color = "var(--ifm-color-primary)" } =
51
55
  iconMap[language] || iconMap[title.toLowerCase()] || {};
52
56
  const tooltipContent = desc ? desc : null;
53
- const cardInner = jsxDEV_7x81h0kn(
54
- Link,
55
- {
56
- to: noteUrl,
57
- className: styles.noteCard,
58
- style: { "--card-index": index, "--note-color": color },
59
- "aria-label": `Read note: ${title}`,
60
- children: [
61
- jsxDEV_7x81h0kn(
62
- "div",
63
- {
64
- className: styles.iconWrapper,
65
- children: jsxDEV_7x81h0kn(
66
- Icon,
67
- { className: styles.noteIcon },
68
- undefined,
69
- false,
70
- undefined,
71
- this,
72
- ),
73
- },
74
- undefined,
75
- false,
76
- undefined,
77
- this,
78
- ),
79
- jsxDEV_7x81h0kn(
80
- "div",
81
- {
82
- className: styles.cardContent,
83
- children: jsxDEV_7x81h0kn(
84
- "h3",
85
- { className: styles.noteTitle, children: title },
86
- undefined,
87
- false,
88
- undefined,
89
- this,
90
- ),
91
- },
92
- undefined,
93
- false,
94
- undefined,
95
- this,
96
- ),
97
- jsxDEV_7x81h0kn(
98
- FaChevronRight,
99
- { className: styles.mobileChevron },
100
- undefined,
101
- false,
102
- undefined,
103
- this,
104
- ),
105
- ],
106
- },
107
- undefined,
108
- true,
109
- undefined,
110
- this,
57
+
58
+ const cardInner = (
59
+ <Link
60
+ to={noteUrl}
61
+ className={styles.noteCard}
62
+ style={{ "--card-index": index, "--note-color": color }}
63
+ aria-label={`Read note: ${title}`}
64
+ >
65
+ <div className={styles.iconWrapper}>
66
+ <Icon className={styles.noteIcon} />
67
+ </div>
68
+ <div className={styles.cardContent}>
69
+ <h3 className={styles.noteTitle}>{title}</h3>
70
+ </div>
71
+ <FaChevronRight className={styles.mobileChevron} />
72
+ </Link>
73
+ );
74
+
75
+ return tooltipContent ? (
76
+ <Tooltip msg={tooltipContent} position="top" underline={false} gap={-8}>
77
+ {cardInner}
78
+ </Tooltip>
79
+ ) : (
80
+ cardInner
111
81
  );
112
- return tooltipContent
113
- ? jsxDEV_7x81h0kn(
114
- Tooltip,
115
- {
116
- msg: tooltipContent,
117
- position: "top",
118
- underline: false,
119
- gap: -8,
120
- children: cardInner,
121
- },
122
- undefined,
123
- false,
124
- undefined,
125
- this,
126
- )
127
- : cardInner;
128
82
  }
83
+
129
84
  export default function NoteCards() {
130
85
  const notes = useNotes();
131
86
  const { path: docsBasePath } = usePluginData(
132
87
  "docusaurus-plugin-content-docs",
133
88
  );
89
+
134
90
  if (!notes.length) return null;
135
- return jsxDEV_7x81h0kn(
136
- "div",
137
- {
138
- className: styles.notesGrid,
139
- role: "list",
140
- children: notes.map((note, index) =>
141
- jsxDEV_7x81h0kn(
142
- NoteCard,
143
- { ...note, index, docsBasePath },
144
- note.slug,
145
- false,
146
- undefined,
147
- this,
148
- ),
149
- ),
150
- },
151
- undefined,
152
- false,
153
- undefined,
154
- this,
91
+
92
+ return (
93
+ <div className={styles.notesGrid} role="list">
94
+ {notes.map((note, index) => (
95
+ <NoteCard
96
+ key={note.slug}
97
+ {...note}
98
+ index={index}
99
+ docsBasePath={docsBasePath}
100
+ />
101
+ ))}
102
+ </div>
155
103
  );
156
104
  }
105
+
157
106
  export function TopicList({
158
107
  desc = "Click on the links below to explore the topics.",
159
108
  style = { marginTop: "-2.5rem", marginBottom: "2.5rem", textAlign: "center" },
160
109
  }) {
161
- return jsxDEV_7x81h0kn(
162
- "div",
163
- {
164
- style,
165
- children: [
166
- jsxDEV_7x81h0kn(
167
- "p",
168
- { dangerouslySetInnerHTML: { __html: desc } },
169
- undefined,
170
- false,
171
- undefined,
172
- this,
173
- ),
174
- jsxDEV_7x81h0kn(DocCardList, {}, undefined, false, undefined, this),
175
- ],
176
- },
177
- undefined,
178
- true,
179
- undefined,
180
- this,
110
+ return (
111
+ <div style={style}>
112
+ <p dangerouslySetInnerHTML={{ __html: desc }} />
113
+ <DocCardList />
114
+ </div>
181
115
  );
182
116
  }
@@ -1,200 +1,55 @@
1
1
  import styles from "../styles.module.css";
2
+
2
3
  export function LoadingState() {
3
- return jsxDEV_7x81h0kn(
4
- "div",
5
- {
6
- className: styles.loading,
7
- children: [
8
- jsxDEV_7x81h0kn(
9
- "div",
10
- {
11
- className: styles.loadingIcon,
12
- children: jsxDEV_7x81h0kn(
13
- "div",
14
- { className: styles.spinner },
15
- undefined,
16
- false,
17
- undefined,
18
- this,
19
- ),
20
- },
21
- undefined,
22
- false,
23
- undefined,
24
- this,
25
- ),
26
- jsxDEV_7x81h0kn(
27
- "div",
28
- {
29
- className: styles.loadingText,
30
- children: [
31
- jsxDEV_7x81h0kn(
32
- "p",
33
- { children: "Preparing preview..." },
34
- undefined,
35
- false,
36
- undefined,
37
- this,
38
- ),
39
- jsxDEV_7x81h0kn(
40
- "span",
41
- { children: "Fetching content from source" },
42
- undefined,
43
- false,
44
- undefined,
45
- this,
46
- ),
47
- ],
48
- },
49
- undefined,
50
- true,
51
- undefined,
52
- this,
53
- ),
54
- ],
55
- },
56
- undefined,
57
- true,
58
- undefined,
59
- this,
4
+ return (
5
+ <div className={styles.loading}>
6
+ <div className={styles.loadingIcon}>
7
+ <div className={styles.spinner} />
8
+ </div>
9
+ <div className={styles.loadingText}>
10
+ <p>Preparing preview...</p>
11
+ <span>Fetching content from source</span>
12
+ </div>
13
+ </div>
60
14
  );
61
15
  }
16
+
62
17
  export function ErrorState({ path, message, fileType, fileUrl, onRetry }) {
63
- return jsxDEV_7x81h0kn(
64
- "div",
65
- {
66
- className: styles.errorState,
67
- children: [
68
- jsxDEV_7x81h0kn(
69
- "div",
70
- { className: styles.errorIcon, children: "⚠️" },
71
- undefined,
72
- false,
73
- undefined,
74
- this,
75
- ),
76
- jsxDEV_7x81h0kn(
77
- "p",
78
- {
79
- children: [
80
- "Could not load: ",
81
- jsxDEV_7x81h0kn(
82
- "code",
83
- { children: path?.split("/").pop() },
84
- undefined,
85
- false,
86
- undefined,
87
- this,
88
- ),
89
- ],
90
- },
91
- undefined,
92
- true,
93
- undefined,
94
- this,
95
- ),
96
- jsxDEV_7x81h0kn(
97
- "p",
98
- { className: styles.errorMsg, children: message },
99
- undefined,
100
- false,
101
- undefined,
102
- this,
103
- ),
104
- jsxDEV_7x81h0kn(
105
- "div",
106
- {
107
- className: styles.errorActions,
108
- children: [
109
- jsxDEV_7x81h0kn(
110
- "button",
111
- {
112
- onClick: onRetry,
113
- className: styles.retryButton,
114
- children: "Retry",
115
- },
116
- undefined,
117
- false,
118
- undefined,
119
- this,
120
- ),
121
- fileType === "web" &&
122
- jsxDEV_7x81h0kn(
123
- "a",
124
- {
125
- href: fileUrl,
126
- target: "_blank",
127
- rel: "noopener",
128
- className: styles.visitButton,
129
- children: "Visit Website",
130
- },
131
- undefined,
132
- false,
133
- undefined,
134
- this,
135
- ),
136
- ],
137
- },
138
- undefined,
139
- true,
140
- undefined,
141
- this,
142
- ),
143
- ],
144
- },
145
- undefined,
146
- true,
147
- undefined,
148
- this,
18
+ return (
19
+ <div className={styles.errorState}>
20
+ <div className={styles.errorIcon}>⚠️</div>
21
+ <p>
22
+ Could not load: <code>{path?.split("/").pop()}</code>
23
+ </p>
24
+ <p className={styles.errorMsg}>{message}</p>
25
+ <div className={styles.errorActions}>
26
+ <button onClick={onRetry} className={styles.retryButton}>
27
+ Retry
28
+ </button>
29
+ {fileType === "web" && (
30
+ <a
31
+ href={fileUrl}
32
+ target="_blank"
33
+ rel="noopener"
34
+ className={styles.visitButton}
35
+ >
36
+ Visit Website
37
+ </a>
38
+ )}
39
+ </div>
40
+ </div>
149
41
  );
150
42
  }
43
+
151
44
  export function OfflineState({ onRetry }) {
152
- return jsxDEV_7x81h0kn(
153
- "div",
154
- {
155
- className: styles.errorState,
156
- children: [
157
- jsxDEV_7x81h0kn(
158
- "div",
159
- { className: styles.errorIcon, children: "\uD83D\uDCE1" },
160
- undefined,
161
- false,
162
- undefined,
163
- this,
164
- ),
165
- jsxDEV_7x81h0kn(
166
- "h3",
167
- { children: "No Connection" },
168
- undefined,
169
- false,
170
- undefined,
171
- this,
172
- ),
173
- jsxDEV_7x81h0kn(
174
- "p",
175
- { children: "This resource requires an active internet connection." },
176
- undefined,
177
- false,
178
- undefined,
179
- this,
180
- ),
181
- jsxDEV_7x81h0kn(
182
- "button",
183
- {
184
- onClick: onRetry,
185
- className: styles.retryButton,
186
- children: "Try Again",
187
- },
188
- undefined,
189
- false,
190
- undefined,
191
- this,
192
- ),
193
- ],
194
- },
195
- undefined,
196
- true,
197
- undefined,
198
- this,
45
+ return (
46
+ <div className={styles.errorState}>
47
+ <div className={styles.errorIcon}>📡</div>
48
+ <h3>No Connection</h3>
49
+ <p>This resource requires an active internet connection.</p>
50
+ <button onClick={onRetry} className={styles.retryButton}>
51
+ Try Again
52
+ </button>
53
+ </div>
199
54
  );
200
55
  }
@@ -1,7 +1,9 @@
1
1
  import React, { useRef, useEffect } from "react";
2
2
  import styles from "../styles.module.css";
3
+
3
4
  export default function FileTabs({ sources, activeIndex, onSelect }) {
4
5
  const tabRefs = useRef([]);
6
+
5
7
  useEffect(() => {
6
8
  const el = tabRefs.current[activeIndex];
7
9
  if (el) {
@@ -12,30 +14,21 @@ export default function FileTabs({ sources, activeIndex, onSelect }) {
12
14
  });
13
15
  }
14
16
  }, [activeIndex]);
17
+
15
18
  if (!sources || sources.length <= 1) return null;
16
- return jsxDEV_7x81h0kn(
17
- "div",
18
- {
19
- className: styles.tabs,
20
- children: sources.map((src, i) =>
21
- jsxDEV_7x81h0kn(
22
- "button",
23
- {
24
- ref: (el) => (tabRefs.current[i] = el),
25
- className: `${styles.tab} ${i === activeIndex ? styles.activeTab : ""}`,
26
- onClick: () => onSelect(i),
27
- children: src.label || src.path.split("/").pop(),
28
- },
29
- i,
30
- false,
31
- undefined,
32
- this,
33
- ),
34
- ),
35
- },
36
- undefined,
37
- false,
38
- undefined,
39
- this,
19
+
20
+ return (
21
+ <div className={styles.tabs}>
22
+ {sources.map((src, i) => (
23
+ <button
24
+ key={i}
25
+ ref={(el) => (tabRefs.current[i] = el)}
26
+ className={`${styles.tab} ${i === activeIndex ? styles.activeTab : ""}`}
27
+ onClick={() => onSelect(i)}
28
+ >
29
+ {src.label || src.path.split("/").pop()}
30
+ </button>
31
+ ))}
32
+ </div>
40
33
  );
41
34
  }