veslx 0.1.3 → 0.1.4
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/dist/client/App.js +17 -0
- package/dist/client/App.js.map +1 -0
- package/dist/client/components/front-matter.js +33 -0
- package/dist/client/components/front-matter.js.map +1 -0
- package/dist/client/components/gallery/components/figure-caption.js +14 -0
- package/dist/client/components/gallery/components/figure-caption.js.map +1 -0
- package/dist/client/components/gallery/components/figure-header.js +13 -0
- package/dist/client/components/gallery/components/figure-header.js.map +1 -0
- package/dist/client/components/gallery/components/lightbox.js +96 -0
- package/dist/client/components/gallery/components/lightbox.js.map +1 -0
- package/dist/client/components/gallery/components/loading-image.js +46 -0
- package/dist/client/components/gallery/components/loading-image.js.map +1 -0
- package/dist/client/components/gallery/hooks/use-gallery-images.js +84 -0
- package/dist/client/components/gallery/hooks/use-gallery-images.js.map +1 -0
- package/dist/client/components/gallery/hooks/use-lightbox.js +37 -0
- package/dist/client/components/gallery/hooks/use-lightbox.js.map +1 -0
- package/dist/client/components/gallery/index.js +96 -0
- package/dist/client/components/gallery/index.js.map +1 -0
- package/dist/client/components/gallery/lib/render-math-in-text.js +42 -0
- package/dist/client/components/gallery/lib/render-math-in-text.js.map +1 -0
- package/dist/client/components/header.js +60 -0
- package/dist/client/components/header.js.map +1 -0
- package/dist/client/components/loading.js +15 -0
- package/dist/client/components/loading.js.map +1 -0
- package/dist/client/components/mdx-components.js +134 -0
- package/dist/client/components/mdx-components.js.map +1 -0
- package/dist/client/components/mode-toggle.js +39 -0
- package/dist/client/components/mode-toggle.js.map +1 -0
- package/dist/client/components/page-error.js +39 -0
- package/dist/client/components/page-error.js.map +1 -0
- package/dist/client/components/parameter-badge.js +48 -0
- package/dist/client/components/parameter-badge.js.map +1 -0
- package/dist/client/components/parameter-table.js +301 -0
- package/dist/client/components/parameter-table.js.map +1 -0
- package/dist/client/components/post-list.js +119 -0
- package/dist/client/components/post-list.js.map +1 -0
- package/dist/client/components/running-bar.js +15 -0
- package/dist/client/components/running-bar.js.map +1 -0
- package/dist/client/components/slides-renderer.js +75 -0
- package/dist/client/components/slides-renderer.js.map +1 -0
- package/dist/client/components/theme-provider.js +9 -0
- package/dist/client/components/theme-provider.js.map +1 -0
- package/dist/client/components/ui/button.js +49 -0
- package/dist/client/components/ui/button.js.map +1 -0
- package/dist/client/components/ui/carousel.js +195 -0
- package/dist/client/components/ui/carousel.js.map +1 -0
- package/dist/client/components/ui/dropdown-menu.js +132 -0
- package/dist/client/components/ui/dropdown-menu.js.map +1 -0
- package/dist/client/hooks/use-key-bindings.js +40 -0
- package/dist/client/hooks/use-key-bindings.js.map +1 -0
- package/dist/client/hooks/use-mdx-content.js +78 -0
- package/dist/client/hooks/use-mdx-content.js.map +1 -0
- package/dist/client/lib/constants.js +5 -0
- package/dist/client/lib/constants.js.map +1 -0
- package/dist/client/lib/format-date.js +8 -0
- package/dist/client/lib/format-date.js.map +1 -0
- package/dist/client/lib/parameter-utils.js +110 -0
- package/dist/client/lib/parameter-utils.js.map +1 -0
- package/dist/client/lib/utils.js +9 -0
- package/dist/client/lib/utils.js.map +1 -0
- package/dist/client/logo_dark.png +0 -0
- package/dist/client/logo_light.png +0 -0
- package/dist/client/main.js +9 -0
- package/dist/client/main.js.map +1 -0
- package/dist/client/pages/home.js +30 -0
- package/dist/client/pages/home.js.map +1 -0
- package/dist/client/pages/post.js +53 -0
- package/dist/client/pages/post.js.map +1 -0
- package/dist/client/pages/slides.js +137 -0
- package/dist/client/pages/slides.js.map +1 -0
- package/dist/client/plugin/src/client.js +185 -0
- package/dist/client/plugin/src/client.js.map +1 -0
- package/package.json +8 -4
- package/src/main.tsx +1 -1
- package/vite.config.ts +22 -3
- package/vite.lib.config.ts +47 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
3
|
+
import { FULLSCREEN_DATA_ATTR } from "../lib/constants.js";
|
|
4
|
+
import { useParams, useSearchParams } from "react-router-dom";
|
|
5
|
+
import Loading from "../components/loading.js";
|
|
6
|
+
import { FrontMatter } from "../components/front-matter.js";
|
|
7
|
+
import { RunningBar } from "../components/running-bar.js";
|
|
8
|
+
import { Header } from "../components/header.js";
|
|
9
|
+
import { useMDXSlides } from "../hooks/use-mdx-content.js";
|
|
10
|
+
import { useSlidesFromMDX, SlideContent } from "../components/slides-renderer.js";
|
|
11
|
+
function SlidesPage() {
|
|
12
|
+
const { "path": path = "." } = useParams();
|
|
13
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
14
|
+
const { Content, frontmatter, loading, error } = useMDXSlides(path);
|
|
15
|
+
const { slides } = useSlidesFromMDX({ Content, frontmatter });
|
|
16
|
+
const totalSlides = slides.length + 1;
|
|
17
|
+
const initialSlide = Math.max(0, Math.min(
|
|
18
|
+
parseInt(searchParams.get("slide") || "0", 10),
|
|
19
|
+
totalSlides - 1
|
|
20
|
+
));
|
|
21
|
+
const [currentSlide, setCurrentSlide] = useState(initialSlide);
|
|
22
|
+
const slideRefs = useRef([]);
|
|
23
|
+
const goToSlide = useCallback((index) => {
|
|
24
|
+
const clampedIndex = Math.max(0, Math.min(index, totalSlides - 1));
|
|
25
|
+
const target = slideRefs.current[clampedIndex];
|
|
26
|
+
if (target) {
|
|
27
|
+
const targetTop = target.getBoundingClientRect().top + window.scrollY;
|
|
28
|
+
window.scrollTo({ top: targetTop, behavior: "smooth" });
|
|
29
|
+
}
|
|
30
|
+
}, [totalSlides]);
|
|
31
|
+
const goToPrevious = useCallback(() => {
|
|
32
|
+
goToSlide(currentSlide - 1);
|
|
33
|
+
}, [currentSlide, goToSlide]);
|
|
34
|
+
const goToNext = useCallback(() => {
|
|
35
|
+
goToSlide(currentSlide + 1);
|
|
36
|
+
}, [currentSlide, goToSlide]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const handleKeyDown = (e) => {
|
|
39
|
+
if (e.key === "ArrowUp" || e.key === "k") {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
goToPrevious();
|
|
42
|
+
} else if (e.key === "ArrowDown" || e.key === "j") {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
goToNext();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
48
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
49
|
+
}, [goToPrevious, goToNext]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
let observer = null;
|
|
52
|
+
const timeoutId = setTimeout(() => {
|
|
53
|
+
observer = new IntersectionObserver(
|
|
54
|
+
(entries) => {
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (entry.isIntersecting) {
|
|
57
|
+
const index = slideRefs.current.findIndex((ref) => ref === entry.target);
|
|
58
|
+
if (index !== -1) {
|
|
59
|
+
setCurrentSlide(index);
|
|
60
|
+
setSearchParams(index > 0 ? { slide: String(index) } : {}, { replace: true });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{ threshold: 0.5 }
|
|
66
|
+
);
|
|
67
|
+
slideRefs.current.forEach((ref) => {
|
|
68
|
+
if (ref) observer.observe(ref);
|
|
69
|
+
});
|
|
70
|
+
}, 100);
|
|
71
|
+
return () => {
|
|
72
|
+
clearTimeout(timeoutId);
|
|
73
|
+
observer == null ? void 0 : observer.disconnect();
|
|
74
|
+
};
|
|
75
|
+
}, [slides.length, setSearchParams]);
|
|
76
|
+
if (loading) {
|
|
77
|
+
return /* @__PURE__ */ jsx(Loading, {});
|
|
78
|
+
}
|
|
79
|
+
if (error) {
|
|
80
|
+
return /* @__PURE__ */ jsx("main", { className: "min-h-screen bg-background container mx-auto max-w-4xl py-12", children: /* @__PURE__ */ jsx("p", { className: "text-center text-red-600", children: error.message }) });
|
|
81
|
+
}
|
|
82
|
+
if (slides.length === 0) {
|
|
83
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-12 text-muted-foreground font-mono text-sm", children: 'no slides found — use "---" to separate slides' });
|
|
84
|
+
}
|
|
85
|
+
return /* @__PURE__ */ jsxs("main", { className: "slides-container", children: [
|
|
86
|
+
/* @__PURE__ */ jsx("title", { children: frontmatter == null ? void 0 : frontmatter.title }),
|
|
87
|
+
/* @__PURE__ */ jsx(RunningBar, {}),
|
|
88
|
+
/* @__PURE__ */ jsx(
|
|
89
|
+
Header,
|
|
90
|
+
{
|
|
91
|
+
slideControls: {
|
|
92
|
+
current: currentSlide,
|
|
93
|
+
total: totalSlides,
|
|
94
|
+
onPrevious: goToPrevious,
|
|
95
|
+
onNext: goToNext
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsxs("div", { ...{ [FULLSCREEN_DATA_ATTR]: "true" }, children: [
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
ref: (el) => {
|
|
104
|
+
slideRefs.current[0] = el;
|
|
105
|
+
},
|
|
106
|
+
className: "slide-page max-w-xl min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto",
|
|
107
|
+
children: /* @__PURE__ */ jsx(
|
|
108
|
+
FrontMatter,
|
|
109
|
+
{
|
|
110
|
+
title: frontmatter == null ? void 0 : frontmatter.title,
|
|
111
|
+
date: frontmatter == null ? void 0 : frontmatter.date,
|
|
112
|
+
description: frontmatter == null ? void 0 : frontmatter.description
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
/* @__PURE__ */ jsx("hr", { className: "print:hidden" }),
|
|
118
|
+
slides.map((slideContent, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
"div",
|
|
121
|
+
{
|
|
122
|
+
ref: (el) => {
|
|
123
|
+
slideRefs.current[index + 1] = el;
|
|
124
|
+
},
|
|
125
|
+
className: "slide-page min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto",
|
|
126
|
+
children: /* @__PURE__ */ jsx(SlideContent, { children: slideContent })
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
index < slides.length - 1 && /* @__PURE__ */ jsx("hr", { className: "print:hidden" })
|
|
130
|
+
] }, index))
|
|
131
|
+
] })
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
SlidesPage
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=slides.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slides.js","sources":["../../../src/pages/slides.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { FULLSCREEN_DATA_ATTR } from \"@/lib/constants\";\nimport { useParams, useSearchParams } from \"react-router-dom\"\nimport Loading from \"@/components/loading\";\nimport { FrontMatter } from \"@/components/front-matter\";\nimport { RunningBar } from \"@/components/running-bar\";\nimport { Header } from \"@/components/header\";\nimport { useMDXSlides } from \"@/hooks/use-mdx-content\";\nimport { useSlidesFromMDX, SlideContent } from \"@/components/slides-renderer\";\n\n\nexport function SlidesPage() {\n const { \"path\": path = \".\" } = useParams();\n const [searchParams, setSearchParams] = useSearchParams();\n\n // Load the compiled MDX module\n const { Content, frontmatter, loading, error } = useMDXSlides(path);\n\n // Split the MDX content into slides by <hr> elements\n const { slides } = useSlidesFromMDX({ Content, frontmatter });\n\n // Total slides = 1 (title) + content slides\n const totalSlides = slides.length + 1;\n\n // Get initial slide from query param\n const initialSlide = Math.max(0, Math.min(\n parseInt(searchParams.get(\"slide\") || \"0\", 10),\n totalSlides - 1\n ));\n\n const [currentSlide, setCurrentSlide] = useState(initialSlide);\n const slideRefs = useRef<(HTMLDivElement | null)[]>([]);\n\n // Scroll to slide and update query param\n const goToSlide = useCallback((index: number) => {\n const clampedIndex = Math.max(0, Math.min(index, totalSlides - 1));\n const target = slideRefs.current[clampedIndex];\n if (target) {\n const targetTop = target.getBoundingClientRect().top + window.scrollY;\n window.scrollTo({ top: targetTop, behavior: \"smooth\" });\n }\n }, [totalSlides]);\n\n const goToPrevious = useCallback(() => {\n goToSlide(currentSlide - 1);\n }, [currentSlide, goToSlide]);\n\n const goToNext = useCallback(() => {\n goToSlide(currentSlide + 1);\n }, [currentSlide, goToSlide]);\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"ArrowUp\" || e.key === \"k\") {\n e.preventDefault();\n goToPrevious();\n } else if (e.key === \"ArrowDown\" || e.key === \"j\") {\n e.preventDefault();\n goToNext();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [goToPrevious, goToNext]);\n\n // Update query param on scroll (delayed to avoid interference on load)\n useEffect(() => {\n let observer: IntersectionObserver | null = null;\n\n const timeoutId = setTimeout(() => {\n observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const index = slideRefs.current.findIndex((ref) => ref === entry.target);\n if (index !== -1) {\n setCurrentSlide(index);\n setSearchParams(index > 0 ? { slide: String(index) } : {}, { replace: true });\n }\n }\n }\n },\n { threshold: 0.5 }\n );\n\n slideRefs.current.forEach((ref) => {\n if (ref) observer!.observe(ref);\n });\n }, 100);\n\n return () => {\n clearTimeout(timeoutId);\n observer?.disconnect();\n };\n }, [slides.length, setSearchParams]);\n\n if (loading) {\n return <Loading />\n }\n\n if (error) {\n return (\n <main className=\"min-h-screen bg-background container mx-auto max-w-4xl py-12\">\n <p className=\"text-center text-red-600\">{error.message}</p>\n </main>\n )\n }\n\n if (slides.length === 0) {\n return (\n <div className=\"flex items-center justify-center p-12 text-muted-foreground font-mono text-sm\">\n no slides found — use \"---\" to separate slides\n </div>\n );\n }\n\n return (\n <main className=\"slides-container\">\n <title>{frontmatter?.title}</title>\n <RunningBar />\n <Header\n slideControls={{\n current: currentSlide,\n total: totalSlides,\n onPrevious: goToPrevious,\n onNext: goToNext,\n }}\n />\n <div {...{[FULLSCREEN_DATA_ATTR]: \"true\"}}>\n {/* Title slide */}\n <div\n ref={(el) => { slideRefs.current[0] = el; }}\n className=\"slide-page max-w-xl min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto\"\n >\n <FrontMatter\n title={frontmatter?.title}\n date={frontmatter?.date}\n description={frontmatter?.description}\n />\n </div>\n <hr className=\"print:hidden\" />\n\n {/* Content slides */}\n {slides.map((slideContent, index) => (\n <div key={index}>\n <div\n ref={(el) => { slideRefs.current[index + 1] = el; }}\n className=\"slide-page min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto\"\n >\n <SlideContent>{slideContent}</SlideContent>\n </div>\n {index < slides.length - 1 && <hr className=\"print:hidden\" />}\n </div>\n ))}\n </div>\n </main>\n )\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
async function parsePath(directory, path) {
|
|
3
|
+
const parts = path === "." ? [] : path.split("/").filter(Boolean);
|
|
4
|
+
let file = null;
|
|
5
|
+
let currentDir = directory;
|
|
6
|
+
for (let i = 0; i < parts.length; i++) {
|
|
7
|
+
const part = parts[i];
|
|
8
|
+
const isLastPart = i === parts.length - 1;
|
|
9
|
+
if (isLastPart) {
|
|
10
|
+
const matchedFile = currentDir.children.find(
|
|
11
|
+
(child) => child.type === "file" && child.name === part
|
|
12
|
+
);
|
|
13
|
+
if (matchedFile) {
|
|
14
|
+
file = matchedFile;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const nextDir = currentDir.children.find(
|
|
19
|
+
(child) => child.type === "directory" && child.name === part
|
|
20
|
+
);
|
|
21
|
+
if (!nextDir) {
|
|
22
|
+
throw new Error(`Path not found: ${path}`);
|
|
23
|
+
}
|
|
24
|
+
currentDir = nextDir;
|
|
25
|
+
}
|
|
26
|
+
return { directory: currentDir, file };
|
|
27
|
+
}
|
|
28
|
+
function findReadme(directory) {
|
|
29
|
+
const readme = directory.children.find(
|
|
30
|
+
(child) => child.type === "file" && [
|
|
31
|
+
"README.md",
|
|
32
|
+
"Readme.md",
|
|
33
|
+
"readme.md",
|
|
34
|
+
"README.mdx",
|
|
35
|
+
"Readme.mdx",
|
|
36
|
+
"readme.mdx"
|
|
37
|
+
].includes(child.name)
|
|
38
|
+
);
|
|
39
|
+
return readme || null;
|
|
40
|
+
}
|
|
41
|
+
function findSlides(directory) {
|
|
42
|
+
const readme = directory.children.find(
|
|
43
|
+
(child) => child.type === "file" && [
|
|
44
|
+
"SLIDES.md",
|
|
45
|
+
"Slides.md",
|
|
46
|
+
"slides.md",
|
|
47
|
+
"SLIDES.mdx",
|
|
48
|
+
"Slides.mdx",
|
|
49
|
+
"slides.mdx"
|
|
50
|
+
].includes(child.name)
|
|
51
|
+
);
|
|
52
|
+
return readme || null;
|
|
53
|
+
}
|
|
54
|
+
function useDirectory(path = ".") {
|
|
55
|
+
const [directory, setDirectory] = useState(null);
|
|
56
|
+
const [file, setFile] = useState(null);
|
|
57
|
+
const [loading, setLoading] = useState(true);
|
|
58
|
+
const [error, setError] = useState(null);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
setLoading(true);
|
|
61
|
+
setError(null);
|
|
62
|
+
(async () => {
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetch(`/raw/.veslx.json`);
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
if (response.status === 404) {
|
|
67
|
+
setError({ type: "config_not_found", message: ".veslx.json not found" });
|
|
68
|
+
} else {
|
|
69
|
+
setError({ type: "fetch_error", message: `Failed to fetch: ${response.status} ${response.statusText}` });
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let json;
|
|
74
|
+
try {
|
|
75
|
+
json = await response.json();
|
|
76
|
+
} catch {
|
|
77
|
+
setError({ type: "parse_error", message: "Failed to parse .veslx.json" });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = await parsePath(json, path);
|
|
83
|
+
} catch {
|
|
84
|
+
setError({ type: "path_not_found", message: `Path not found: ${path}`, status: 404 });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
parsed.directory.children.sort((a, b) => {
|
|
88
|
+
let aDate, bDate;
|
|
89
|
+
if (a.children) {
|
|
90
|
+
const readme = findReadme(a);
|
|
91
|
+
if (readme && readme.frontmatter && readme.frontmatter.date) {
|
|
92
|
+
aDate = new Date(readme.frontmatter.date);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (b.children) {
|
|
96
|
+
const readme = findReadme(b);
|
|
97
|
+
if (readme && readme.frontmatter && readme.frontmatter.date) {
|
|
98
|
+
bDate = new Date(readme.frontmatter.date);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (aDate && bDate) {
|
|
102
|
+
return bDate.getTime() - aDate.getTime();
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
});
|
|
106
|
+
setDirectory(parsed.directory);
|
|
107
|
+
setFile(parsed.file);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
setError({ type: "fetch_error", message: err.message || "Unknown error" });
|
|
110
|
+
} finally {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
return () => {
|
|
115
|
+
};
|
|
116
|
+
}, [path]);
|
|
117
|
+
return { directory, file, loading, error };
|
|
118
|
+
}
|
|
119
|
+
function useFileContent(path) {
|
|
120
|
+
const [blob, setBlob] = useState(null);
|
|
121
|
+
const [content, setContent] = useState(null);
|
|
122
|
+
const [loading, setLoading] = useState(true);
|
|
123
|
+
const [error, setError] = useState(null);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
setLoading(true);
|
|
127
|
+
setError(null);
|
|
128
|
+
(async () => {
|
|
129
|
+
try {
|
|
130
|
+
const res = await fetch(`/raw/${path}`, {
|
|
131
|
+
signal: controller.signal
|
|
132
|
+
});
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);
|
|
135
|
+
}
|
|
136
|
+
const fetchedBlob = await res.blob();
|
|
137
|
+
setBlob(fetchedBlob);
|
|
138
|
+
try {
|
|
139
|
+
const text = await fetchedBlob.text();
|
|
140
|
+
setContent(text);
|
|
141
|
+
} catch {
|
|
142
|
+
setContent(null);
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
setError(err instanceof Error ? err.message : "Unknown error");
|
|
149
|
+
} finally {
|
|
150
|
+
setLoading(false);
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
return () => controller.abort();
|
|
154
|
+
}, [path]);
|
|
155
|
+
return { blob, content, loading, error };
|
|
156
|
+
}
|
|
157
|
+
function isSimulationRunning() {
|
|
158
|
+
const [running, setRunning] = useState(false);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
let interval;
|
|
161
|
+
const fetchStatus = async () => {
|
|
162
|
+
const response = await fetch(`/raw/.running`);
|
|
163
|
+
const text = await response.text();
|
|
164
|
+
if (text === "") {
|
|
165
|
+
setRunning(true);
|
|
166
|
+
} else {
|
|
167
|
+
setRunning(false);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
fetchStatus();
|
|
171
|
+
interval = setInterval(fetchStatus, 1e3);
|
|
172
|
+
return () => {
|
|
173
|
+
clearInterval(interval);
|
|
174
|
+
};
|
|
175
|
+
}, []);
|
|
176
|
+
return running;
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
findReadme,
|
|
180
|
+
findSlides,
|
|
181
|
+
isSimulationRunning,
|
|
182
|
+
useDirectory,
|
|
183
|
+
useFileContent
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../plugin/src/client.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { DirectoryEntry, FileEntry } from \"./lib\";\n\nasync function parsePath(directory: DirectoryEntry, path: string): Promise<{ directory: DirectoryEntry; file: FileEntry | null }> {\n const parts = path === \".\" ? [] : path.split(\"/\").filter(Boolean);\n\n let file = null;\n let currentDir = directory;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const isLastPart = i === parts.length - 1;\n\n // Check if this part matches a file (only on last part)\n if (isLastPart) {\n const matchedFile = currentDir.children.find(\n (child) => child.type === \"file\" && child.name === part\n ) as FileEntry | undefined;\n\n if (matchedFile) {\n file = matchedFile;\n break;\n }\n }\n\n // Otherwise, look for a directory\n const nextDir = currentDir.children.find(\n (child) => child.type === \"directory\" && child.name === part\n ) as DirectoryEntry | undefined;\n\n if (!nextDir) {\n throw new Error(`Path not found: ${path}`);\n }\n\n currentDir = nextDir;\n }\n\n return { directory: currentDir, file };\n}\n\nexport function findReadme(directory: DirectoryEntry): FileEntry | null {\n const readme = directory.children.find((child) => \n child.type === \"file\" && \n [\n \"README.md\", \"Readme.md\", \"readme.md\",\n \"README.mdx\", \"Readme.mdx\", \"readme.mdx\"\n ].includes(child.name)\n ) as FileEntry | undefined;\n\n return readme || null;\n}\n\nexport function findSlides(directory: DirectoryEntry): FileEntry | null {\n const readme = directory.children.find((child) =>\n child.type === \"file\" &&\n [\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\"\n ].includes(child.name)\n ) as FileEntry | undefined;\n\n return readme || null;\n}\n\n\nexport type DirectoryError =\n | { type: 'config_not_found'; message: string }\n | { type: 'path_not_found'; message: string; status: 404 }\n | { type: 'fetch_error'; message: string }\n | { type: 'parse_error'; message: string };\n\nexport function useDirectory(path: string = \".\") {\n const [directory, setDirectory] = useState<DirectoryEntry | null>(null);\n const [file, setFile] = useState<FileEntry | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<DirectoryError | null>(null);\n\n useEffect(() => {\n setLoading(true);\n setError(null);\n\n (async () => {\n try {\n const response = await fetch(`/raw/.veslx.json`);\n\n if (!response.ok) {\n if (response.status === 404) {\n setError({ type: 'config_not_found', message: '.veslx.json not found' });\n } else {\n setError({ type: 'fetch_error', message: `Failed to fetch: ${response.status} ${response.statusText}` });\n }\n return;\n }\n\n let json;\n try {\n json = await response.json();\n } catch {\n setError({ type: 'parse_error', message: 'Failed to parse .veslx.json' });\n return;\n }\n\n let parsed: { directory: DirectoryEntry; file: FileEntry | null };\n try {\n parsed = await parsePath(json, path);\n } catch {\n setError({ type: 'path_not_found', message: `Path not found: ${path}`, status: 404 });\n return;\n }\n\n parsed.directory.children.sort((a: any, b: any) => {\n let aDate, bDate;\n if (a.children) {\n const readme = findReadme(a);\n if (readme && readme.frontmatter && readme.frontmatter.date) {\n aDate = new Date(readme.frontmatter.date as string | number | Date)\n }\n }\n if (b.children) {\n const readme = findReadme(b);\n if (readme && readme.frontmatter && readme.frontmatter.date) {\n bDate = new Date(readme.frontmatter.date as string | number | Date)\n }\n }\n if (aDate && bDate) {\n return bDate.getTime() - aDate.getTime()\n }\n return 0;\n });\n\n setDirectory(parsed.directory);\n setFile(parsed.file);\n } catch (err: any) {\n setError({ type: 'fetch_error', message: err.message || 'Unknown error' });\n } finally {\n setLoading(false);\n }\n })();\n\n return () => {};\n }, [path]);\n\n return { directory, file, loading, error };\n}\n\nexport function useFileContent(path: string) {\n const [blob, setBlob] = useState<Blob | null>(null);\n const [content, setContent] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n setLoading(true);\n setError(null);\n\n (async () => {\n try {\n const res = await fetch(`/raw/${path}`, {\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);\n }\n\n const fetchedBlob = await res.blob();\n setBlob(fetchedBlob);\n\n // Try to read as text - some binary files may fail\n try {\n const text = await fetchedBlob.text();\n setContent(text);\n } catch {\n // Binary file - text content not available\n setContent(null);\n }\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n return; // Ignore abort errors\n }\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n })();\n\n return () => controller.abort();\n }, [path]);\n\n return { blob, content, loading, error };\n}\n\nexport function isSimulationRunning() {\n const [running, setRunning] = useState<boolean>(false);\n\n useEffect(() => {\n let interval: ReturnType<typeof setInterval>;\n\n const fetchStatus = async () => {\n const response = await fetch(`/raw/.running`);\n\n // this is an elaborate workaround to stop devtools logging errors on 404s\n const text = await response.text()\n if (text === \"\") {\n setRunning(true);\n } else {\n setRunning(false);\n }\n };\n\n // Initial fetch\n fetchStatus();\n\n // Poll every second\n interval = setInterval(fetchStatus, 1000);\n\n return () => {\n clearInterval(interval);\n };\n }, []);\n\n return running;\n}"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veslx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"veslx": "bin/veslx.ts"
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
"src",
|
|
13
13
|
"public",
|
|
14
14
|
"index.html",
|
|
15
|
-
"vite.config.ts"
|
|
15
|
+
"vite.config.ts",
|
|
16
|
+
"vite.lib.config.ts"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"dev": "bun bin/veslx.ts serve",
|
|
19
|
-
"build": "bun bin/veslx.ts build"
|
|
20
|
+
"build": "bun bin/veslx.ts build",
|
|
21
|
+
"build:lib": "vite build --config vite.lib.config.ts",
|
|
22
|
+
"prepublishOnly": "npm run build:lib"
|
|
20
23
|
},
|
|
21
24
|
"dependencies": {
|
|
22
25
|
"acorn": "^8.15.0",
|
|
@@ -82,7 +85,8 @@
|
|
|
82
85
|
"eslint-plugin-react-refresh": "^0.4.24",
|
|
83
86
|
"globals": "^16.5.0",
|
|
84
87
|
"typescript": "~5.9.3",
|
|
85
|
-
"typescript-eslint": "^8.46.3"
|
|
88
|
+
"typescript-eslint": "^8.46.3",
|
|
89
|
+
"vite-plugin-dts": "^4.5.4"
|
|
86
90
|
},
|
|
87
91
|
"overrides": {}
|
|
88
92
|
}
|
package/src/main.tsx
CHANGED
package/vite.config.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { defineConfig } from 'vite'
|
|
3
2
|
import react from '@vitejs/plugin-react'
|
|
4
3
|
import tailwindcss from '@tailwindcss/vite'
|
|
@@ -10,9 +9,16 @@ import remarkGfm from 'remark-gfm'
|
|
|
10
9
|
import remarkMdxFrontmatter from 'remark-mdx-frontmatter'
|
|
11
10
|
import { fileURLToPath } from 'url'
|
|
12
11
|
import path from 'path'
|
|
12
|
+
import fs from 'fs'
|
|
13
13
|
|
|
14
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
15
15
|
|
|
16
|
+
// Use pre-built client if available (published package), otherwise use src/ (local dev)
|
|
17
|
+
const distClientPath = path.join(__dirname, 'dist/client')
|
|
18
|
+
const srcPath = path.join(__dirname, 'src')
|
|
19
|
+
const usePrebuilt = fs.existsSync(path.join(distClientPath, 'main.js'))
|
|
20
|
+
const clientPath = usePrebuilt ? distClientPath : srcPath
|
|
21
|
+
|
|
16
22
|
export default defineConfig({
|
|
17
23
|
clearScreen: false,
|
|
18
24
|
cacheDir: path.join(__dirname, 'node_modules/.vite'),
|
|
@@ -37,7 +43,7 @@ export default defineConfig({
|
|
|
37
43
|
],
|
|
38
44
|
resolve: {
|
|
39
45
|
alias: {
|
|
40
|
-
'@':
|
|
46
|
+
'@': clientPath,
|
|
41
47
|
},
|
|
42
48
|
},
|
|
43
49
|
server: {
|
|
@@ -60,7 +66,7 @@ export default defineConfig({
|
|
|
60
66
|
reportCompressedSize: false,
|
|
61
67
|
},
|
|
62
68
|
optimizeDeps: {
|
|
63
|
-
entries: [path.join(
|
|
69
|
+
entries: [path.join(clientPath, usePrebuilt ? 'main.js' : 'main.tsx')],
|
|
64
70
|
include: [
|
|
65
71
|
'react',
|
|
66
72
|
'react-dom',
|
|
@@ -69,6 +75,19 @@ export default defineConfig({
|
|
|
69
75
|
'react/jsx-dev-runtime',
|
|
70
76
|
'@mdx-js/react',
|
|
71
77
|
'react-router-dom',
|
|
78
|
+
// Pre-bundle heavy UI deps
|
|
79
|
+
'@radix-ui/react-collapsible',
|
|
80
|
+
'@radix-ui/react-dialog',
|
|
81
|
+
'@radix-ui/react-dropdown-menu',
|
|
82
|
+
'@radix-ui/react-scroll-area',
|
|
83
|
+
'@radix-ui/react-select',
|
|
84
|
+
'@radix-ui/react-tooltip',
|
|
85
|
+
'lucide-react',
|
|
86
|
+
'embla-carousel-react',
|
|
87
|
+
'next-themes',
|
|
88
|
+
'clsx',
|
|
89
|
+
'tailwind-merge',
|
|
90
|
+
'class-variance-authority',
|
|
72
91
|
],
|
|
73
92
|
},
|
|
74
93
|
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { resolve } from 'path'
|
|
4
|
+
|
|
5
|
+
// Library build config - pre-compiles src/ components for distribution
|
|
6
|
+
// CSS is left to runtime processing (Tailwind needs to scan for classes)
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
react(),
|
|
10
|
+
],
|
|
11
|
+
build: {
|
|
12
|
+
lib: {
|
|
13
|
+
entry: resolve(__dirname, 'src/main.tsx'),
|
|
14
|
+
formats: ['es'],
|
|
15
|
+
fileName: 'main'
|
|
16
|
+
},
|
|
17
|
+
rollupOptions: {
|
|
18
|
+
// Externalize all deps - they're resolved at runtime
|
|
19
|
+
external: (id) => {
|
|
20
|
+
// Virtual modules
|
|
21
|
+
if (id === 'virtual:content-modules') return true
|
|
22
|
+
// CSS files (processed at runtime by Tailwind)
|
|
23
|
+
if (id.endsWith('.css')) return true
|
|
24
|
+
// All node_modules
|
|
25
|
+
if (id.includes('node_modules')) return true
|
|
26
|
+
// Bare imports (dependencies)
|
|
27
|
+
if (!id.startsWith('.') && !id.startsWith('/') && !id.startsWith('@/')) return true
|
|
28
|
+
return false
|
|
29
|
+
},
|
|
30
|
+
output: {
|
|
31
|
+
preserveModules: true,
|
|
32
|
+
preserveModulesRoot: 'src',
|
|
33
|
+
entryFileNames: '[name].js',
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
outDir: 'dist/client',
|
|
37
|
+
emptyOutDir: true,
|
|
38
|
+
// Don't minify for better debugging
|
|
39
|
+
minify: false,
|
|
40
|
+
sourcemap: true,
|
|
41
|
+
},
|
|
42
|
+
resolve: {
|
|
43
|
+
alias: {
|
|
44
|
+
'@': resolve(__dirname, 'src'),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
})
|