@portosaur/theme 0.1.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 (76) hide show
  1. package/README.md +13 -0
  2. package/assets/img/icon-old.png +0 -0
  3. package/assets/img/icon.png +0 -0
  4. package/assets/img/project-blank.png +0 -0
  5. package/assets/img/social-card.jpeg +0 -0
  6. package/assets/img/svg/icon-blog.svg +2 -0
  7. package/assets/img/svg/icon-close.svg +3 -0
  8. package/assets/img/svg/icon-dock.svg +4 -0
  9. package/assets/img/svg/icon-link.svg +5 -0
  10. package/assets/img/svg/icon-note.svg +2 -0
  11. package/assets/img/svg/icon-popup.svg +1 -0
  12. package/assets/img/svg/icon-save.svg +5 -0
  13. package/assets/img/svg/icon.svg +240 -0
  14. package/assets/img/svg/project-blank.svg +140 -0
  15. package/assets/sample-resume.pdf +0 -0
  16. package/package.json +41 -0
  17. package/plugins/README.md +8 -0
  18. package/src/index.d.ts +11 -0
  19. package/src/index.mjs +14 -0
  20. package/src/plugins/theme.mjs +13 -0
  21. package/theme/DocCategoryGeneratedIndexPage/index.jsx +15 -0
  22. package/theme/MDXComponents.jsx +19 -0
  23. package/theme/README.md +9 -0
  24. package/theme/Root.jsx +11 -0
  25. package/theme/components/AboutSection/index.jsx +264 -0
  26. package/theme/components/AboutSection/styles.module.css +309 -0
  27. package/theme/components/ContactSection/index.jsx +188 -0
  28. package/theme/components/ContactSection/styles.module.css +343 -0
  29. package/theme/components/ExperienceSection/index.jsx +119 -0
  30. package/theme/components/ExperienceSection/styles.module.css +183 -0
  31. package/theme/components/HeroSection/index.jsx +198 -0
  32. package/theme/components/HeroSection/styles.module.css +484 -0
  33. package/theme/components/NavArrow/index.jsx +124 -0
  34. package/theme/components/NavArrow/styles.module.css +107 -0
  35. package/theme/components/NoteIndex/index.jsx +182 -0
  36. package/theme/components/NoteIndex/styles.module.css +167 -0
  37. package/theme/components/Preview/components/FeedbackStates.jsx +200 -0
  38. package/theme/components/Preview/components/FileTabs.jsx +41 -0
  39. package/theme/components/Preview/components/PreviewContent.jsx +104 -0
  40. package/theme/components/Preview/components/PreviewHeader.jsx +411 -0
  41. package/theme/components/Preview/components/Triggers/Pv.jsx +253 -0
  42. package/theme/components/Preview/components/Triggers/SrcPv.jsx +55 -0
  43. package/theme/components/Preview/components/Triggers/index.jsx +2 -0
  44. package/theme/components/Preview/components/ViewerWindow.jsx +489 -0
  45. package/theme/components/Preview/hooks/useAdaptiveSizing.jsx +90 -0
  46. package/theme/components/Preview/hooks/useDeepLinkHash.jsx +24 -0
  47. package/theme/components/Preview/hooks/useDockLayout.jsx +86 -0
  48. package/theme/components/Preview/hooks/useFileFetch.jsx +38 -0
  49. package/theme/components/Preview/hooks/useTouchZoom.jsx +98 -0
  50. package/theme/components/Preview/index.jsx +3 -0
  51. package/theme/components/Preview/renderers/CodeRenderer.jsx +124 -0
  52. package/theme/components/Preview/renderers/ImageRenderer.jsx +74 -0
  53. package/theme/components/Preview/renderers/PdfRenderer.jsx +93 -0
  54. package/theme/components/Preview/renderers/WebRenderer.jsx +59 -0
  55. package/theme/components/Preview/state/index.jsx +177 -0
  56. package/theme/components/Preview/styles.module.css +776 -0
  57. package/theme/components/Preview/utils/index.jsx +62 -0
  58. package/theme/components/ProjectsSection/index.jsx +790 -0
  59. package/theme/components/ProjectsSection/styles.module.css +900 -0
  60. package/theme/components/SocialLinks/index.jsx +115 -0
  61. package/theme/components/SocialLinks/styles.module.css +57 -0
  62. package/theme/components/Tooltip/index.jsx +104 -0
  63. package/theme/components/Tooltip/styles.module.css +168 -0
  64. package/theme/config/iconMappings.jsx +427 -0
  65. package/theme/config/prism.jsx +72 -0
  66. package/theme/config/sidebar.jsx +11 -0
  67. package/theme/css/bootstrap.css +5 -0
  68. package/theme/css/catppuccin.css +618 -0
  69. package/theme/css/custom.css +253 -0
  70. package/theme/css/tasks.css +874 -0
  71. package/theme/hooks/useScrollReveal.jsx +20 -0
  72. package/theme/pages/index.jsx +104 -0
  73. package/theme/pages/notes.jsx +131 -0
  74. package/theme/pages/tasks.jsx +989 -0
  75. package/theme/utils/HashNavigation.jsx +185 -0
  76. package/theme/utils/updateTitle.jsx +65 -0
@@ -0,0 +1,411 @@
1
+ import React, { useState, useRef } from "react";
2
+ import Tooltip from "../../Tooltip/index.js";
3
+ import styles from "../styles.module.css";
4
+ import { PreviewMode } from "../state/index.js";
5
+ import IconDock from "../../../assets/img/svg/icon-dock.svg";
6
+ import IconPopup from "../../../assets/img/svg/icon-popup.svg";
7
+ import IconSave from "../../../assets/img/svg/icon-save.svg";
8
+ import IconLink from "../../../assets/img/svg/icon-link.svg";
9
+ import IconClose from "../../../assets/img/svg/icon-close.svg";
10
+ export default function PreviewHeader({
11
+ displayTitle,
12
+ fileType,
13
+ fileUrl,
14
+ mode,
15
+ zoomLevel,
16
+ onZoomChange,
17
+ onToggleMode,
18
+ onClose,
19
+ onDownload,
20
+ isDownloading,
21
+ modeSwitch = true,
22
+ showDockLabel = true,
23
+ }) {
24
+ const [showZoomMenu, setShowZoomMenu] = useState(false);
25
+ const zoomMenuRef = useRef(null);
26
+ const zoomMenuTimer = useRef(null);
27
+ const isMobileSize =
28
+ typeof window !== "undefined" && window.innerWidth <= 768;
29
+ const toggleLabel =
30
+ mode === "popup" ? "Dock" : mode === "dock" ? "PiP" : "Dock";
31
+ const toggleTooltip =
32
+ mode === "popup"
33
+ ? "Dock to side"
34
+ : mode === "dock"
35
+ ? "Open as PiP"
36
+ : "Dock to side";
37
+ return jsxDEV_7x81h0kn(
38
+ Fragment_8vg9x3sq,
39
+ {
40
+ children: [
41
+ mode === "dock" &&
42
+ !isMobileSize &&
43
+ jsxDEV_7x81h0kn(
44
+ "div",
45
+ {
46
+ className: styles.revealHeader,
47
+ children: jsxDEV_7x81h0kn(
48
+ "h1",
49
+ {
50
+ className: styles.popupTitle,
51
+ children: jsxDEV_7x81h0kn(
52
+ "span",
53
+ { className: styles.primaryText, children: "Preview " },
54
+ undefined,
55
+ false,
56
+ undefined,
57
+ this,
58
+ ),
59
+ },
60
+ undefined,
61
+ false,
62
+ undefined,
63
+ this,
64
+ ),
65
+ },
66
+ undefined,
67
+ false,
68
+ undefined,
69
+ this,
70
+ ),
71
+ jsxDEV_7x81h0kn(
72
+ "div",
73
+ {
74
+ className: styles.popupHeader,
75
+ children: [
76
+ jsxDEV_7x81h0kn(
77
+ "div",
78
+ {
79
+ className: styles.headerLeft,
80
+ children: jsxDEV_7x81h0kn(
81
+ "h4",
82
+ {
83
+ className: styles.popupTitle,
84
+ children: jsxDEV_7x81h0kn(
85
+ "span",
86
+ {
87
+ className: styles.baseTitleText,
88
+ children: displayTitle,
89
+ },
90
+ undefined,
91
+ false,
92
+ undefined,
93
+ this,
94
+ ),
95
+ },
96
+ undefined,
97
+ false,
98
+ undefined,
99
+ this,
100
+ ),
101
+ },
102
+ undefined,
103
+ false,
104
+ undefined,
105
+ this,
106
+ ),
107
+ jsxDEV_7x81h0kn(
108
+ "div",
109
+ {
110
+ className: styles.headerControls,
111
+ children: [
112
+ !isMobileSize &&
113
+ fileType !== "web" &&
114
+ jsxDEV_7x81h0kn(
115
+ "div",
116
+ {
117
+ className: styles.zoomDropdown,
118
+ ref: zoomMenuRef,
119
+ onMouseEnter: () => {
120
+ if (zoomMenuTimer.current)
121
+ clearTimeout(zoomMenuTimer.current);
122
+ setShowZoomMenu(true);
123
+ },
124
+ onMouseLeave: () => {
125
+ zoomMenuTimer.current = setTimeout(
126
+ () => setShowZoomMenu(false),
127
+ 150,
128
+ );
129
+ },
130
+ children: [
131
+ jsxDEV_7x81h0kn(
132
+ "button",
133
+ {
134
+ onClick: () => setShowZoomMenu(!showZoomMenu),
135
+ className: styles.zoomVal,
136
+ title: "Change Zoom",
137
+ children: [
138
+ Math.round(zoomLevel * 100),
139
+ "%",
140
+ jsxDEV_7x81h0kn(
141
+ "span",
142
+ {
143
+ className: styles.dropdownArrow,
144
+ children: "▼",
145
+ },
146
+ undefined,
147
+ false,
148
+ undefined,
149
+ this,
150
+ ),
151
+ ],
152
+ },
153
+ undefined,
154
+ true,
155
+ undefined,
156
+ this,
157
+ ),
158
+ showZoomMenu &&
159
+ jsxDEV_7x81h0kn(
160
+ "div",
161
+ {
162
+ className: styles.zoomMenu,
163
+ children: [0.5, 0.75, 1, 1.25, 1.5, 2].map(
164
+ (level) =>
165
+ jsxDEV_7x81h0kn(
166
+ "button",
167
+ {
168
+ className: `${styles.zoomMenuItem} ${zoomLevel === level ? styles.zoomMenuItemActive : ""}`,
169
+ onClick: () => {
170
+ onZoomChange(level);
171
+ setShowZoomMenu(false);
172
+ },
173
+ children:
174
+ level === 1
175
+ ? "100% (Fit)"
176
+ : `${Math.round(level * 100)}%`,
177
+ },
178
+ level,
179
+ false,
180
+ undefined,
181
+ this,
182
+ ),
183
+ ),
184
+ },
185
+ undefined,
186
+ false,
187
+ undefined,
188
+ this,
189
+ ),
190
+ ],
191
+ },
192
+ undefined,
193
+ true,
194
+ undefined,
195
+ this,
196
+ ),
197
+ fileType === "web"
198
+ ? jsxDEV_7x81h0kn(
199
+ Tooltip,
200
+ {
201
+ msg: "Open externally",
202
+ position: "bottom",
203
+ underline: false,
204
+ children: jsxDEV_7x81h0kn(
205
+ "a",
206
+ {
207
+ href: fileUrl,
208
+ target: "_blank",
209
+ rel: "noopener noreferrer",
210
+ className: styles.headerAction,
211
+ children: [
212
+ jsxDEV_7x81h0kn(
213
+ IconLink,
214
+ { className: styles.headerIcon },
215
+ undefined,
216
+ false,
217
+ undefined,
218
+ this,
219
+ ),
220
+ jsxDEV_7x81h0kn(
221
+ "span",
222
+ {
223
+ className: styles.btnText,
224
+ children: "Visit",
225
+ },
226
+ undefined,
227
+ false,
228
+ undefined,
229
+ this,
230
+ ),
231
+ ],
232
+ },
233
+ undefined,
234
+ true,
235
+ undefined,
236
+ this,
237
+ ),
238
+ },
239
+ undefined,
240
+ false,
241
+ undefined,
242
+ this,
243
+ )
244
+ : jsxDEV_7x81h0kn(
245
+ Tooltip,
246
+ {
247
+ msg: isDownloading
248
+ ? "Downloading..."
249
+ : "Download file",
250
+ position: "bottom",
251
+ underline: false,
252
+ children: jsxDEV_7x81h0kn(
253
+ "button",
254
+ {
255
+ onClick: onDownload,
256
+ disabled: isDownloading,
257
+ className: `${styles.headerAction} ${styles.downloadButton} ${isDownloading ? styles.headerActionDisabled : ""}`,
258
+ children: [
259
+ isDownloading
260
+ ? jsxDEV_7x81h0kn(
261
+ "div",
262
+ { className: styles.spinnerSmall },
263
+ undefined,
264
+ false,
265
+ undefined,
266
+ this,
267
+ )
268
+ : jsxDEV_7x81h0kn(
269
+ IconSave,
270
+ { className: styles.headerIconSmall },
271
+ undefined,
272
+ false,
273
+ undefined,
274
+ this,
275
+ ),
276
+ jsxDEV_7x81h0kn(
277
+ "span",
278
+ {
279
+ className: styles.btnText,
280
+ children: isDownloading
281
+ ? "Saving"
282
+ : "Save",
283
+ },
284
+ undefined,
285
+ false,
286
+ undefined,
287
+ this,
288
+ ),
289
+ ],
290
+ },
291
+ undefined,
292
+ true,
293
+ undefined,
294
+ this,
295
+ ),
296
+ },
297
+ undefined,
298
+ false,
299
+ undefined,
300
+ this,
301
+ ),
302
+ modeSwitch &&
303
+ jsxDEV_7x81h0kn(
304
+ Tooltip,
305
+ {
306
+ msg: toggleTooltip,
307
+ position: "bottom",
308
+ underline: false,
309
+ children: jsxDEV_7x81h0kn(
310
+ "button",
311
+ {
312
+ onClick: onToggleMode,
313
+ className: `${styles.headerAction} ${styles.dockToggle}`,
314
+ children: [
315
+ mode === "popup" || mode === "pip"
316
+ ? jsxDEV_7x81h0kn(
317
+ IconDock,
318
+ { className: styles.headerIcon },
319
+ undefined,
320
+ false,
321
+ undefined,
322
+ this,
323
+ )
324
+ : jsxDEV_7x81h0kn(
325
+ IconPopup,
326
+ {
327
+ className: `${styles.headerIcon} ${styles.iconPopupTweak}`,
328
+ },
329
+ undefined,
330
+ false,
331
+ undefined,
332
+ this,
333
+ ),
334
+ showDockLabel &&
335
+ jsxDEV_7x81h0kn(
336
+ "span",
337
+ {
338
+ className: styles.btnText,
339
+ children: toggleLabel,
340
+ },
341
+ undefined,
342
+ false,
343
+ undefined,
344
+ this,
345
+ ),
346
+ ],
347
+ },
348
+ undefined,
349
+ true,
350
+ undefined,
351
+ this,
352
+ ),
353
+ },
354
+ undefined,
355
+ false,
356
+ undefined,
357
+ this,
358
+ ),
359
+ jsxDEV_7x81h0kn(
360
+ Tooltip,
361
+ {
362
+ msg: "Close",
363
+ position: "bottom",
364
+ underline: false,
365
+ children: jsxDEV_7x81h0kn(
366
+ "button",
367
+ {
368
+ onClick: onClose,
369
+ className: `${styles.headerAction} ${styles.headerActionClose}`,
370
+ children: jsxDEV_7x81h0kn(
371
+ IconClose,
372
+ { className: styles.headerIconSmall },
373
+ undefined,
374
+ false,
375
+ undefined,
376
+ this,
377
+ ),
378
+ },
379
+ undefined,
380
+ false,
381
+ undefined,
382
+ this,
383
+ ),
384
+ },
385
+ undefined,
386
+ false,
387
+ undefined,
388
+ this,
389
+ ),
390
+ ],
391
+ },
392
+ undefined,
393
+ true,
394
+ undefined,
395
+ this,
396
+ ),
397
+ ],
398
+ },
399
+ undefined,
400
+ true,
401
+ undefined,
402
+ this,
403
+ ),
404
+ ],
405
+ },
406
+ undefined,
407
+ true,
408
+ undefined,
409
+ this,
410
+ );
411
+ }
@@ -0,0 +1,253 @@
1
+ import React, { useEffect, useMemo } from "react";
2
+ import { useLocation } from "@docusaurus/router";
3
+ import { usePreview } from "../../state";
4
+ import Tooltip from "../../../Tooltip";
5
+ import {
6
+ generatePvSlug,
7
+ generatePvHash,
8
+ parsePvHash,
9
+ classify,
10
+ } from "../../utils";
11
+ import styles from "../../styles.module.css";
12
+ export function normalizeSources({
13
+ href,
14
+ path,
15
+ sources,
16
+ children,
17
+ desc,
18
+ title,
19
+ id,
20
+ }) {
21
+ const rawSources =
22
+ sources && sources.length > 0
23
+ ? sources
24
+ : href || path
25
+ ? [{ path: (href || path).trim(), label: null, desc }]
26
+ : [];
27
+ return rawSources.map((src) => {
28
+ const sPath = (src.path || src.href || "").trim();
29
+ const sDesc = src.desc || "";
30
+ const childrenText = React.Children.toArray(children)
31
+ .map((c) => (typeof c === "string" || typeof c === "number" ? c : ""))
32
+ .join("")
33
+ .trim();
34
+ const label = src.label || childrenText;
35
+ let urlLabel = "";
36
+ let domain = "";
37
+ let type = "text";
38
+ if (sPath) {
39
+ type = classify(sPath);
40
+ const cleanPath = sPath.split(/[?#]/)[0].toLowerCase();
41
+ urlLabel = cleanPath.split("/").filter(Boolean).pop();
42
+ try {
43
+ if (sPath.startsWith("http") || sPath.startsWith("//")) {
44
+ const url = new URL(
45
+ sPath.startsWith("//") ? `https:${sPath}` : sPath,
46
+ );
47
+ domain = url.hostname.replace("www.", "");
48
+ }
49
+ } catch (e) {}
50
+ }
51
+ const source = domain || urlLabel || "Local";
52
+ const displayLabel = label || source;
53
+ const tooltip = sDesc || null;
54
+ return {
55
+ path: sPath,
56
+ label: displayLabel,
57
+ domain,
58
+ type,
59
+ source,
60
+ tooltip,
61
+ id: src.id || id,
62
+ title: src.title || title,
63
+ };
64
+ });
65
+ }
66
+ export default function Pv(props) {
67
+ const {
68
+ children,
69
+ id: manualId,
70
+ activeIdx = 0,
71
+ sources: overrideSources,
72
+ title,
73
+ mode = "popup",
74
+ modeSwitch = true,
75
+ underline = true,
76
+ } = props;
77
+ const hasSingleSource = !!(props.href || props.path);
78
+ const hasMultiSource = !!(overrideSources && overrideSources.length > 0);
79
+ if (!hasSingleSource && !hasMultiSource) {
80
+ console.error(
81
+ "<Pv> component requires either 'href', 'path', or 'sources' prop.",
82
+ );
83
+ return jsxDEV_7x81h0kn(
84
+ "span",
85
+ { style: { color: "red" }, children: "[Preview Error: Missing href]" },
86
+ undefined,
87
+ false,
88
+ undefined,
89
+ this,
90
+ );
91
+ }
92
+ if (hasSingleSource && hasMultiSource) {
93
+ console.error(
94
+ "<Pv> component cannot accept both 'href' and 'sources'. Choose one.",
95
+ );
96
+ return jsxDEV_7x81h0kn(
97
+ "span",
98
+ { style: { color: "red" }, children: "[Preview Error: Conflict]" },
99
+ undefined,
100
+ false,
101
+ undefined,
102
+ this,
103
+ );
104
+ }
105
+ const {
106
+ isOpen,
107
+ mode: currentMode,
108
+ sources: activeSources,
109
+ activeIndex,
110
+ openPreview,
111
+ closePreview,
112
+ setMode,
113
+ } = usePreview();
114
+ const location = useLocation();
115
+ const srcList = useMemo(
116
+ () => overrideSources || normalizeSources(props),
117
+ [props, overrideSources, title],
118
+ );
119
+ const baseSlug = useMemo(() => {
120
+ if (manualId) return generatePvSlug(manualId);
121
+ if (title) return generatePvSlug(title);
122
+ const pathOrHref = props.href || props.path || srcList[activeIdx]?.path;
123
+ if (pathOrHref) {
124
+ const filename = pathOrHref
125
+ .split(/[?#]/)[0]
126
+ .split("/")
127
+ .filter(Boolean)
128
+ .pop();
129
+ if (filename) return generatePvSlug(filename);
130
+ }
131
+ const childrenText = typeof children === "string" ? children.trim() : null;
132
+ if (childrenText) return generatePvSlug(childrenText);
133
+ return "preview";
134
+ }, [manualId, title, props.href, props.path, srcList, activeIdx, children]);
135
+ useEffect(() => {
136
+ const timer = setTimeout(() => {
137
+ const parsed = parsePvHash(window.location.hash);
138
+ if (parsed && parsed.slug === baseSlug) {
139
+ const hashMode = parsed.mode || mode;
140
+ setMode(hashMode);
141
+ openPreview(
142
+ srcList,
143
+ activeIdx,
144
+ generatePvHash(baseSlug, hashMode),
145
+ hashMode,
146
+ baseSlug,
147
+ modeSwitch,
148
+ );
149
+ }
150
+ }, 150);
151
+ return () => clearTimeout(timer);
152
+ }, [
153
+ location.hash,
154
+ baseSlug,
155
+ srcList,
156
+ openPreview,
157
+ setMode,
158
+ mode,
159
+ activeIdx,
160
+ modeSwitch,
161
+ ]);
162
+ if (srcList.length === 0)
163
+ return jsxDEV_7x81h0kn(
164
+ "span",
165
+ { children },
166
+ undefined,
167
+ false,
168
+ undefined,
169
+ this,
170
+ );
171
+ const isCurrentlyActive =
172
+ isOpen &&
173
+ activeSources.length === srcList.length &&
174
+ activeSources[activeIdx]?.path === srcList[activeIdx]?.path &&
175
+ activeIndex === activeIdx;
176
+ const handleClick = () => {
177
+ if (isCurrentlyActive) {
178
+ closePreview();
179
+ } else {
180
+ setMode(mode);
181
+ openPreview(
182
+ srcList,
183
+ activeIdx,
184
+ generatePvHash(baseSlug, mode),
185
+ mode,
186
+ baseSlug,
187
+ modeSwitch,
188
+ );
189
+ }
190
+ };
191
+ const targetHash = generatePvHash(baseSlug, mode);
192
+ const trigger = jsxDEV_7x81h0kn(
193
+ "a",
194
+ {
195
+ href: `#${targetHash}`,
196
+ className: `${styles.previewTrigger} ${isCurrentlyActive ? styles.activeTrigger : ""} ${!underline ? styles.noUnderline : ""}`,
197
+ onClick: (e) => {
198
+ e.preventDefault();
199
+ handleClick();
200
+ },
201
+ role: "button",
202
+ tabIndex: 0,
203
+ onKeyDown: (e) => {
204
+ if (e.key === "Enter") {
205
+ e.preventDefault();
206
+ handleClick();
207
+ }
208
+ },
209
+ children: children || srcList[activeIdx]?.label,
210
+ },
211
+ undefined,
212
+ false,
213
+ undefined,
214
+ this,
215
+ );
216
+ const hasTooltip = !!srcList[activeIdx]?.tooltip;
217
+ if (!hasTooltip) {
218
+ return jsxDEV_7x81h0kn(
219
+ "span",
220
+ { className: styles.previewContainer, children: trigger },
221
+ undefined,
222
+ false,
223
+ undefined,
224
+ this,
225
+ );
226
+ }
227
+ const tooltipMsg = srcList[activeIdx]?.tooltip;
228
+ return jsxDEV_7x81h0kn(
229
+ "span",
230
+ {
231
+ className: styles.previewContainer,
232
+ children: tooltipMsg
233
+ ? jsxDEV_7x81h0kn(
234
+ Tooltip,
235
+ {
236
+ msg: tooltipMsg,
237
+ position: "top",
238
+ underline: false,
239
+ children: trigger,
240
+ },
241
+ undefined,
242
+ false,
243
+ undefined,
244
+ this,
245
+ )
246
+ : trigger,
247
+ },
248
+ undefined,
249
+ false,
250
+ undefined,
251
+ this,
252
+ );
253
+ }