veslx 0.1.14 → 0.1.15
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 +262 -55
- package/bin/lib/build.ts +65 -13
- package/bin/lib/import-config.ts +10 -9
- package/bin/lib/init.ts +21 -22
- package/bin/lib/serve.ts +66 -12
- package/bin/veslx.ts +2 -2
- package/dist/client/App.js +3 -9
- package/dist/client/App.js.map +1 -1
- package/dist/client/components/front-matter.js +11 -25
- package/dist/client/components/front-matter.js.map +1 -1
- package/dist/client/components/gallery/components/figure-caption.js +6 -4
- package/dist/client/components/gallery/components/figure-caption.js.map +1 -1
- package/dist/client/components/gallery/components/figure-header.js +3 -3
- package/dist/client/components/gallery/components/figure-header.js.map +1 -1
- package/dist/client/components/gallery/components/lightbox.js +13 -13
- package/dist/client/components/gallery/components/lightbox.js.map +1 -1
- package/dist/client/components/gallery/components/loading-image.js +11 -10
- package/dist/client/components/gallery/components/loading-image.js.map +1 -1
- package/dist/client/components/gallery/hooks/use-gallery-images.js +31 -7
- package/dist/client/components/gallery/hooks/use-gallery-images.js.map +1 -1
- package/dist/client/components/gallery/index.js +22 -15
- package/dist/client/components/gallery/index.js.map +1 -1
- package/dist/client/components/header.js +5 -3
- package/dist/client/components/header.js.map +1 -1
- package/dist/client/components/mdx-components.js +42 -8
- package/dist/client/components/mdx-components.js.map +1 -1
- package/dist/client/components/post-list.js +97 -90
- package/dist/client/components/post-list.js.map +1 -1
- package/dist/client/components/running-bar.js +1 -1
- package/dist/client/components/running-bar.js.map +1 -1
- package/dist/client/components/slide.js +18 -0
- package/dist/client/components/slide.js.map +1 -0
- package/dist/client/components/slides-renderer.js +7 -71
- package/dist/client/components/slides-renderer.js.map +1 -1
- package/dist/client/hooks/use-mdx-content.js +55 -9
- package/dist/client/hooks/use-mdx-content.js.map +1 -1
- package/dist/client/main.js +1 -0
- package/dist/client/main.js.map +1 -1
- package/dist/client/pages/content-router.js +19 -0
- package/dist/client/pages/content-router.js.map +1 -0
- package/dist/client/pages/home.js +11 -7
- package/dist/client/pages/home.js.map +1 -1
- package/dist/client/pages/post.js +8 -20
- package/dist/client/pages/post.js.map +1 -1
- package/dist/client/pages/slides.js +62 -86
- package/dist/client/pages/slides.js.map +1 -1
- package/dist/client/plugin/src/client.js +58 -96
- package/dist/client/plugin/src/client.js.map +1 -1
- package/dist/client/plugin/src/directory-tree.js +111 -0
- package/dist/client/plugin/src/directory-tree.js.map +1 -0
- package/index.html +1 -1
- package/package.json +27 -15
- package/plugin/src/client.tsx +64 -116
- package/plugin/src/directory-tree.ts +171 -0
- package/plugin/src/lib.ts +6 -249
- package/plugin/src/plugin.ts +93 -50
- package/plugin/src/remark-slides.ts +100 -0
- package/plugin/src/types.ts +22 -0
- package/src/App.tsx +3 -6
- package/src/components/front-matter.tsx +14 -29
- package/src/components/gallery/components/figure-caption.tsx +15 -7
- package/src/components/gallery/components/figure-header.tsx +3 -3
- package/src/components/gallery/components/lightbox.tsx +15 -13
- package/src/components/gallery/components/loading-image.tsx +15 -12
- package/src/components/gallery/hooks/use-gallery-images.ts +51 -10
- package/src/components/gallery/index.tsx +32 -26
- package/src/components/header.tsx +14 -9
- package/src/components/mdx-components.tsx +61 -8
- package/src/components/post-list.tsx +149 -115
- package/src/components/running-bar.tsx +1 -1
- package/src/components/slide.tsx +22 -5
- package/src/components/slides-renderer.tsx +7 -115
- package/src/components/welcome.tsx +11 -14
- package/src/hooks/use-mdx-content.ts +94 -9
- package/src/index.css +159 -0
- package/src/main.tsx +1 -0
- package/src/pages/content-router.tsx +27 -0
- package/src/pages/home.tsx +16 -2
- package/src/pages/post.tsx +10 -13
- package/src/pages/slides.tsx +75 -88
- package/src/vite-env.d.ts +7 -17
- package/vite.config.ts +25 -6
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
|
+
function findMdxModule(modules, path) {
|
|
3
|
+
const keys = Object.keys(modules);
|
|
4
|
+
const normalizedPath = path.replace(/^\//, "");
|
|
5
|
+
if (normalizedPath.endsWith(".mdx")) {
|
|
6
|
+
const matchingKey = keys.find((key) => {
|
|
7
|
+
if (key.endsWith(`/${normalizedPath}`)) return true;
|
|
8
|
+
if (key === `@content/${normalizedPath}`) return true;
|
|
9
|
+
if (key === `/@content/${normalizedPath}`) return true;
|
|
10
|
+
if (key === normalizedPath) return true;
|
|
11
|
+
if (key === `/${normalizedPath}`) return true;
|
|
12
|
+
return false;
|
|
13
|
+
});
|
|
14
|
+
return matchingKey ? modules[matchingKey] : null;
|
|
15
|
+
}
|
|
16
|
+
const candidates = [
|
|
17
|
+
`${normalizedPath}/index.mdx`,
|
|
18
|
+
`${normalizedPath}/README.mdx`,
|
|
19
|
+
`${normalizedPath}.mdx`
|
|
20
|
+
];
|
|
21
|
+
for (const candidate of candidates) {
|
|
22
|
+
const matchingKey = keys.find((key) => {
|
|
23
|
+
if (key.endsWith(`/${candidate}`)) return true;
|
|
24
|
+
if (key === `@content/${candidate}`) return true;
|
|
25
|
+
if (key === candidate) return true;
|
|
26
|
+
return false;
|
|
27
|
+
});
|
|
28
|
+
if (matchingKey) {
|
|
29
|
+
return modules[matchingKey];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
2
34
|
function useMDXContent(path) {
|
|
3
35
|
const [Content, setContent] = useState(null);
|
|
4
36
|
const [frontmatter, setFrontmatter] = useState(void 0);
|
|
@@ -9,10 +41,7 @@ function useMDXContent(path) {
|
|
|
9
41
|
setLoading(true);
|
|
10
42
|
setError(null);
|
|
11
43
|
import("virtual:content-modules").then(({ modules }) => {
|
|
12
|
-
const
|
|
13
|
-
(key) => key.endsWith(`/${path}/README.mdx`)
|
|
14
|
-
);
|
|
15
|
-
const loader = matchingKey ? modules[matchingKey] : null;
|
|
44
|
+
const loader = findMdxModule(modules, path);
|
|
16
45
|
if (!loader) {
|
|
17
46
|
throw new Error(`MDX module not found for path: ${path}`);
|
|
18
47
|
}
|
|
@@ -35,9 +64,28 @@ function useMDXContent(path) {
|
|
|
35
64
|
}, [path]);
|
|
36
65
|
return { Content, frontmatter, loading, error };
|
|
37
66
|
}
|
|
67
|
+
function findSlidesModule(modules, path) {
|
|
68
|
+
const keys = Object.keys(modules);
|
|
69
|
+
if (path.endsWith(".mdx")) {
|
|
70
|
+
const matchingKey = keys.find((key) => key.endsWith(`/${path}`));
|
|
71
|
+
return matchingKey ? modules[matchingKey] : null;
|
|
72
|
+
}
|
|
73
|
+
const candidates = [
|
|
74
|
+
`/${path}/SLIDES.mdx`,
|
|
75
|
+
`/${path}/index.slides.mdx`
|
|
76
|
+
];
|
|
77
|
+
for (const suffix of candidates) {
|
|
78
|
+
const matchingKey = keys.find((key) => key.endsWith(suffix));
|
|
79
|
+
if (matchingKey) {
|
|
80
|
+
return modules[matchingKey];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
38
85
|
function useMDXSlides(path) {
|
|
39
86
|
const [Content, setContent] = useState(null);
|
|
40
87
|
const [frontmatter, setFrontmatter] = useState(void 0);
|
|
88
|
+
const [slideCount, setSlideCount] = useState(void 0);
|
|
41
89
|
const [loading, setLoading] = useState(true);
|
|
42
90
|
const [error, setError] = useState(null);
|
|
43
91
|
useEffect(() => {
|
|
@@ -45,10 +93,7 @@ function useMDXSlides(path) {
|
|
|
45
93
|
setLoading(true);
|
|
46
94
|
setError(null);
|
|
47
95
|
import("virtual:content-modules").then(({ slides }) => {
|
|
48
|
-
const
|
|
49
|
-
(key) => key.endsWith(`/${path}/SLIDES.mdx`)
|
|
50
|
-
);
|
|
51
|
-
const loader = matchingKey ? slides[matchingKey] : null;
|
|
96
|
+
const loader = findSlidesModule(slides, path);
|
|
52
97
|
if (!loader) {
|
|
53
98
|
throw new Error(`Slides module not found for path: ${path}`);
|
|
54
99
|
}
|
|
@@ -57,6 +102,7 @@ function useMDXSlides(path) {
|
|
|
57
102
|
if (!cancelled) {
|
|
58
103
|
setContent(() => mod.default);
|
|
59
104
|
setFrontmatter(mod.frontmatter);
|
|
105
|
+
setSlideCount(mod.slideCount);
|
|
60
106
|
setLoading(false);
|
|
61
107
|
}
|
|
62
108
|
}).catch((err) => {
|
|
@@ -69,7 +115,7 @@ function useMDXSlides(path) {
|
|
|
69
115
|
cancelled = true;
|
|
70
116
|
};
|
|
71
117
|
}, [path]);
|
|
72
|
-
return { Content, frontmatter, loading, error };
|
|
118
|
+
return { Content, frontmatter, slideCount, loading, error };
|
|
73
119
|
}
|
|
74
120
|
export {
|
|
75
121
|
useMDXContent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-mdx-content.js","sources":["../../../src/hooks/use-mdx-content.ts"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport type { ComponentType } from 'react'\n\ninterface MDXModule {\n default: ComponentType<{ components?: Record<string, ComponentType> }>\n frontmatter?: {\n title?: string\n description?: string\n date?: string\n visibility?: string\n }\n}\n\ntype ModuleLoader = () => Promise<MDXModule>\ntype ModuleMap = Record<string, ModuleLoader>\n\nexport function useMDXContent(path: string) {\n const [Content, setContent] = useState<MDXModule['default'] | null>(null)\n const [frontmatter, setFrontmatter] = useState<MDXModule['frontmatter']>(undefined)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n // Dynamic import to avoid pre-bundling issues\n import('virtual:content-modules')\n .then(({ modules }) => {\n const
|
|
1
|
+
{"version":3,"file":"use-mdx-content.js","sources":["../../../src/hooks/use-mdx-content.ts"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport type { ComponentType } from 'react'\n\ninterface MDXModule {\n default: ComponentType<{ components?: Record<string, ComponentType> }>\n frontmatter?: {\n title?: string\n description?: string\n date?: string\n visibility?: string\n draft?: boolean\n }\n slideCount?: number // Exported by remark-slides plugin for SLIDES.mdx files\n}\n\ntype ModuleLoader = () => Promise<MDXModule>\ntype ModuleMap = Record<string, ModuleLoader>\n\n/**\n * Find MDX module by path. Supports:\n * - Full path: \"docs/intro.mdx\" -> matches exactly\n * - Folder path: \"docs\" -> matches \"docs/index.mdx\", \"docs/README.mdx\", or \"docs.mdx\"\n */\nfunction findMdxModule(modules: ModuleMap, path: string): ModuleLoader | null {\n const keys = Object.keys(modules)\n\n // Normalize path - remove leading slash if present\n const normalizedPath = path.replace(/^\\//, '')\n\n // If path already ends with .mdx, match exactly\n if (normalizedPath.endsWith('.mdx')) {\n // Try multiple matching strategies for different Vite glob formats\n const matchingKey = keys.find(key => {\n // Strategy 1: Key ends with /path (e.g., @content/docs/foo.mdx matches docs/foo.mdx)\n if (key.endsWith(`/${normalizedPath}`)) return true\n // Strategy 2: Key equals @content/path (alias form)\n if (key === `@content/${normalizedPath}`) return true\n // Strategy 3: Key equals /@content/path (with leading slash)\n if (key === `/@content/${normalizedPath}`) return true\n // Strategy 4: Key equals path directly\n if (key === normalizedPath) return true\n // Strategy 5: Key equals /path (with leading slash)\n if (key === `/${normalizedPath}`) return true\n return false\n })\n return matchingKey ? modules[matchingKey] : null\n }\n\n // Otherwise, try folder conventions in order of preference:\n // 1. folder/index.mdx (modern convention)\n // 2. folder/README.mdx (current convention)\n // 3. folder.mdx (file alongside folders)\n const candidates = [\n `${normalizedPath}/index.mdx`,\n `${normalizedPath}/README.mdx`,\n `${normalizedPath}.mdx`,\n ]\n\n for (const candidate of candidates) {\n const matchingKey = keys.find(key => {\n if (key.endsWith(`/${candidate}`)) return true\n if (key === `@content/${candidate}`) return true\n if (key === candidate) return true\n return false\n })\n if (matchingKey) {\n return modules[matchingKey]\n }\n }\n\n return null\n}\n\nexport function useMDXContent(path: string) {\n const [Content, setContent] = useState<MDXModule['default'] | null>(null)\n const [frontmatter, setFrontmatter] = useState<MDXModule['frontmatter']>(undefined)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n // Dynamic import to avoid pre-bundling issues\n import('virtual:content-modules')\n .then(({ modules }) => {\n const loader = findMdxModule(modules as ModuleMap, path)\n\n if (!loader) {\n throw new Error(`MDX module not found for path: ${path}`)\n }\n\n return loader()\n })\n .then((mod) => {\n if (!cancelled) {\n setContent(() => mod.default)\n setFrontmatter(mod.frontmatter)\n setLoading(false)\n }\n })\n .catch((err) => {\n if (!cancelled) {\n setError(err)\n setLoading(false)\n }\n })\n\n return () => {\n cancelled = true\n }\n }, [path])\n\n return { Content, frontmatter, loading, error }\n}\n\n/**\n * Find slides module by path. Supports:\n * - Full path: \"docs/intro.slides.mdx\" or \"docs/SLIDES.mdx\" -> matches exactly\n * - Folder path: \"docs\" -> matches \"docs/SLIDES.mdx\" or \"docs/index.slides.mdx\"\n */\nfunction findSlidesModule(modules: ModuleMap, path: string): ModuleLoader | null {\n const keys = Object.keys(modules)\n\n // If path already ends with .mdx, match exactly\n if (path.endsWith('.mdx')) {\n const matchingKey = keys.find(key => key.endsWith(`/${path}`))\n return matchingKey ? modules[matchingKey] : null\n }\n\n // Otherwise, try folder conventions:\n // 1. folder/SLIDES.mdx (current convention)\n // 2. folder/index.slides.mdx (alternative)\n const candidates = [\n `/${path}/SLIDES.mdx`,\n `/${path}/index.slides.mdx`,\n ]\n\n for (const suffix of candidates) {\n const matchingKey = keys.find(key => key.endsWith(suffix))\n if (matchingKey) {\n return modules[matchingKey]\n }\n }\n\n return null\n}\n\nexport function useMDXSlides(path: string) {\n const [Content, setContent] = useState<MDXModule['default'] | null>(null)\n const [frontmatter, setFrontmatter] = useState<MDXModule['frontmatter']>(undefined)\n const [slideCount, setSlideCount] = useState<number | undefined>(undefined)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n\n // Dynamic import to avoid pre-bundling issues\n import('virtual:content-modules')\n .then(({ slides }) => {\n const loader = findSlidesModule(slides as ModuleMap, path)\n\n if (!loader) {\n throw new Error(`Slides module not found for path: ${path}`)\n }\n\n return loader()\n })\n .then((mod) => {\n if (!cancelled) {\n setContent(() => mod.default)\n setFrontmatter(mod.frontmatter)\n setSlideCount(mod.slideCount)\n setLoading(false)\n }\n })\n .catch((err) => {\n if (!cancelled) {\n setError(err)\n setLoading(false)\n }\n })\n\n return () => {\n cancelled = true\n }\n }, [path])\n\n return { Content, frontmatter, slideCount, loading, error }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/client/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { StrictMode } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
|
+
import "katex/dist/katex.min.css";
|
|
4
5
|
import "./src/index.css";
|
|
5
6
|
import App from "./App.js";
|
|
6
7
|
createRoot(document.getElementById("root")).render(
|
package/dist/client/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sources":["../../src/main.tsx"],"sourcesContent":["import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport './index.css'\nimport App from '@/App'\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <App />\n </StrictMode>,\n)\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.js","sources":["../../src/main.tsx"],"sourcesContent":["import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport 'katex/dist/katex.min.css'\nimport './index.css'\nimport App from '@/App'\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <App />\n </StrictMode>,\n)\n"],"names":[],"mappings":";;;;;;AAMA,WAAW,SAAS,eAAe,MAAM,CAAE,EAAE;AAAA,EAC3C,oBAAC,YAAA,EACC,UAAA,oBAAC,KAAA,CAAA,CAAI,EAAA,CACP;AACF;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
import { Home } from "./home.js";
|
|
4
|
+
import { Post } from "./post.js";
|
|
5
|
+
import { SlidesPage } from "./slides.js";
|
|
6
|
+
function ContentRouter() {
|
|
7
|
+
const { "*": path = "" } = useParams();
|
|
8
|
+
if (path.endsWith(".slides.mdx") || path.endsWith("SLIDES.mdx")) {
|
|
9
|
+
return /* @__PURE__ */ jsx(SlidesPage, {});
|
|
10
|
+
}
|
|
11
|
+
if (path.endsWith(".mdx") || path.endsWith(".md")) {
|
|
12
|
+
return /* @__PURE__ */ jsx(Post, {});
|
|
13
|
+
}
|
|
14
|
+
return /* @__PURE__ */ jsx(Home, {});
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
ContentRouter
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=content-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-router.js","sources":["../../../src/pages/content-router.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\"\nimport { Home } from \"./home\"\nimport { Post } from \"./post\"\nimport { SlidesPage } from \"./slides\"\n\n/**\n * Routes to the appropriate page based on the URL path:\n * - *.slides.mdx or *SLIDES.mdx → SlidesPage\n * - *.mdx → Post\n * - everything else → Home (directory listing)\n */\nexport function ContentRouter() {\n const { \"*\": path = \"\" } = useParams()\n\n // Check if this is a slides file\n if (path.endsWith('.slides.mdx') || path.endsWith('SLIDES.mdx')) {\n return <SlidesPage />\n }\n\n // Check if this is any MDX file\n if (path.endsWith('.mdx') || path.endsWith('.md')) {\n return <Post />\n }\n\n // Otherwise show directory listing\n return <Home />\n}\n"],"names":[],"mappings":";;;;;AAWO,SAAS,gBAAgB;AAC9B,QAAM,EAAE,KAAK,OAAO,GAAA,IAAO,UAAA;AAG3B,MAAI,KAAK,SAAS,aAAa,KAAK,KAAK,SAAS,YAAY,GAAG;AAC/D,+BAAQ,YAAA,EAAW;AAAA,EACrB;AAGA,MAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,KAAK,GAAG;AACjD,+BAAQ,MAAA,EAAK;AAAA,EACf;AAGA,6BAAQ,MAAA,EAAK;AACf;"}
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useParams } from "react-router-dom";
|
|
3
3
|
import { useDirectory } from "../plugin/src/client.js";
|
|
4
|
-
import Loading from "../components/loading.js";
|
|
5
4
|
import PostList from "../components/post-list.js";
|
|
6
5
|
import { ErrorDisplay } from "../components/page-error.js";
|
|
7
6
|
import { RunningBar } from "../components/running-bar.js";
|
|
8
7
|
import { Header } from "../components/header.js";
|
|
8
|
+
import siteConfig from "virtual:veslx-config";
|
|
9
9
|
function Home() {
|
|
10
10
|
const { "*": path = "." } = useParams();
|
|
11
|
-
const { directory,
|
|
11
|
+
const { directory, error } = useDirectory(path);
|
|
12
|
+
const config = siteConfig;
|
|
12
13
|
if (error) {
|
|
13
14
|
return /* @__PURE__ */ jsx(ErrorDisplay, { error, path });
|
|
14
15
|
}
|
|
15
|
-
if (loading) {
|
|
16
|
-
return /* @__PURE__ */ jsx(Loading, {});
|
|
17
|
-
}
|
|
18
16
|
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen flex-col bg-background noise-overlay", children: [
|
|
19
17
|
/* @__PURE__ */ jsx(RunningBar, {}),
|
|
20
18
|
/* @__PURE__ */ jsx(Header, {}),
|
|
21
19
|
/* @__PURE__ */ jsxs("main", { className: "flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]", children: [
|
|
22
|
-
/* @__PURE__ */ jsx("title", { children:
|
|
23
|
-
/* @__PURE__ */
|
|
20
|
+
/* @__PURE__ */ jsx("title", { children: path === "." || path === "" ? config.name : `${config.name} - ${path}` }),
|
|
21
|
+
/* @__PURE__ */ jsxs("main", { className: "flex flex-col gap-8 mb-32 mt-32", children: [
|
|
22
|
+
(path === "." || path === "") && /* @__PURE__ */ jsxs("div", { className: "animate-fade-in", children: [
|
|
23
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl md:text-3xl font-semibold tracking-tight text-foreground", children: config.name }),
|
|
24
|
+
config.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: config.description })
|
|
25
|
+
] }),
|
|
26
|
+
directory && /* @__PURE__ */ jsx("div", { className: "animate-fade-in", children: /* @__PURE__ */ jsx(PostList, { directory }) })
|
|
27
|
+
] })
|
|
24
28
|
] })
|
|
25
29
|
] });
|
|
26
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home.js","sources":["../../../src/pages/home.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\"\nimport { useDirectory } from \"../../plugin/src/client\";\nimport Loading from \"@/components/loading\";\nimport PostList from \"@/components/post-list\";\nimport { ErrorDisplay } from \"@/components/page-error\";\nimport { RunningBar } from \"@/components/running-bar\";\nimport { Header } from \"@/components/header\";\n\nexport function Home() {\n const { \"*\": path = \".\" } = useParams();\n const { directory, loading, error } = useDirectory(path)\n\n if (error) {\n return <ErrorDisplay error={error} path={path} />;\n }\n\n if (loading) {\n return (\n <Loading />\n )\n }\n\n return (\n <div className=\"flex min-h-screen flex-col bg-background noise-overlay\">\n <RunningBar />\n <Header />\n <main className=\"flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]\">\n <title>{
|
|
1
|
+
{"version":3,"file":"home.js","sources":["../../../src/pages/home.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\"\nimport { useDirectory } from \"../../plugin/src/client\";\nimport Loading from \"@/components/loading\";\nimport PostList from \"@/components/post-list\";\nimport { ErrorDisplay } from \"@/components/page-error\";\nimport { RunningBar } from \"@/components/running-bar\";\nimport { Header } from \"@/components/header\";\nimport siteConfig from \"virtual:veslx-config\";\n\nexport function Home() {\n const { \"*\": path = \".\" } = useParams();\n const { directory, loading, error } = useDirectory(path)\n const config = siteConfig;\n\n if (error) {\n return <ErrorDisplay error={error} path={path} />;\n }\n\n if (loading) {\n return (\n <Loading />\n )\n }\n\n return (\n <div className=\"flex min-h-screen flex-col bg-background noise-overlay\">\n <RunningBar />\n <Header />\n <main className=\"flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]\">\n <title>{(path === \".\" || path === \"\") ? config.name : `${config.name} - ${path}`}</title>\n <main className=\"flex flex-col gap-8 mb-32 mt-32\">\n {(path === \".\" || path === \"\") && (\n <div className=\"animate-fade-in\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-foreground\">\n {config.name}\n </h1>\n {config.description && (\n <p className=\"mt-2 text-muted-foreground\">\n {config.description}\n </p>\n )}\n </div>\n )}\n {directory && (\n <div className=\"animate-fade-in\">\n <PostList directory={directory}/>\n </div>\n )}\n </main>\n </main>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AASO,SAAS,OAAO;AACrB,QAAM,EAAE,KAAK,OAAO,IAAA,IAAQ,UAAA;AAC5B,QAAM,EAAE,WAAoB,UAAU,aAAa,IAAI;AACvD,QAAM,SAAS;AAEf,MAAI,OAAO;AACT,WAAO,oBAAC,cAAA,EAAa,OAAc,KAAA,CAAY;AAAA,EACjD;AAQA,SACE,qBAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,IAAA,oBAAC,YAAA,EAAW;AAAA,wBACX,QAAA,EAAO;AAAA,IACR,qBAAC,QAAA,EAAK,WAAU,+EACd,UAAA;AAAA,MAAA,oBAAC,SAAA,EAAQ,UAAA,SAAS,OAAO,SAAS,KAAM,OAAO,OAAO,GAAG,OAAO,IAAI,MAAM,IAAI,IAAG;AAAA,MACjF,qBAAC,QAAA,EAAK,WAAU,mCACZ,UAAA;AAAA,SAAA,SAAS,OAAO,SAAS,OACzB,qBAAC,OAAA,EAAI,WAAU,mBACb,UAAA;AAAA,UAAA,oBAAC,MAAA,EAAG,WAAU,qEACX,UAAA,OAAO,MACV;AAAA,UACC,OAAO,eACN,oBAAC,OAAE,WAAU,8BACV,iBAAO,YAAA,CACV;AAAA,QAAA,GAEJ;AAAA,QAED,iCACE,OAAA,EAAI,WAAU,mBACb,UAAA,oBAAC,UAAA,EAAS,WAAqB,EAAA,CACjC;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -2,22 +2,21 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useParams } from "react-router-dom";
|
|
3
3
|
import { useDirectory, isSimulationRunning, findSlides } from "../plugin/src/client.js";
|
|
4
4
|
import Loading from "../components/loading.js";
|
|
5
|
-
import { FrontMatter } from "../components/front-matter.js";
|
|
6
5
|
import { RunningBar } from "../components/running-bar.js";
|
|
7
6
|
import { Header } from "../components/header.js";
|
|
8
7
|
import { useMDXContent } from "../hooks/use-mdx-content.js";
|
|
9
8
|
import { mdxComponents } from "../components/mdx-components.js";
|
|
10
9
|
function Post() {
|
|
11
|
-
const { "
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
10
|
+
const { "*": rawPath = "." } = useParams();
|
|
11
|
+
const mdxPath = rawPath;
|
|
12
|
+
const dirPath = mdxPath.replace(/\/[^/]+\.mdx$/, "") || ".";
|
|
13
|
+
const { directory } = useDirectory(dirPath);
|
|
14
|
+
const { Content, frontmatter, loading: mdxLoading, error } = useMDXContent(mdxPath);
|
|
15
15
|
const isRunning = isSimulationRunning();
|
|
16
|
-
let slides = null;
|
|
17
16
|
if (directory) {
|
|
18
|
-
|
|
17
|
+
findSlides(directory);
|
|
19
18
|
}
|
|
20
|
-
const loading =
|
|
19
|
+
const loading = mdxLoading;
|
|
21
20
|
if (loading) return /* @__PURE__ */ jsx(Loading, {});
|
|
22
21
|
if (error) {
|
|
23
22
|
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 }) });
|
|
@@ -32,18 +31,7 @@ function Post() {
|
|
|
32
31
|
/* @__PURE__ */ jsx("span", { className: "uppercase tracking-widest", children: "simulation running" }),
|
|
33
32
|
/* @__PURE__ */ jsx("span", { className: "text-primary-foreground/60", children: "Page will auto-refresh on completion" })
|
|
34
33
|
] }) }),
|
|
35
|
-
Content && /* @__PURE__ */
|
|
36
|
-
/* @__PURE__ */ jsx(
|
|
37
|
-
FrontMatter,
|
|
38
|
-
{
|
|
39
|
-
title: frontmatter == null ? void 0 : frontmatter.title,
|
|
40
|
-
date: frontmatter == null ? void 0 : frontmatter.date,
|
|
41
|
-
description: frontmatter == null ? void 0 : frontmatter.description,
|
|
42
|
-
slides
|
|
43
|
-
}
|
|
44
|
-
),
|
|
45
|
-
/* @__PURE__ */ jsx(Content, { components: mdxComponents })
|
|
46
|
-
] })
|
|
34
|
+
Content && /* @__PURE__ */ jsx("article", { className: "my-24 prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline max-w-[var(--prose-width)] animate-fade-in", children: /* @__PURE__ */ jsx(Content, { components: mdxComponents }) })
|
|
47
35
|
] })
|
|
48
36
|
] });
|
|
49
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post.js","sources":["../../../src/pages/post.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { findSlides, isSimulationRunning, useDirectory } from \"../../plugin/src/client\";\nimport Loading from \"@/components/loading\";\nimport { FileEntry } from \"plugin/src/lib\";\nimport {
|
|
1
|
+
{"version":3,"file":"post.js","sources":["../../../src/pages/post.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { findSlides, isSimulationRunning, useDirectory } from \"../../plugin/src/client\";\nimport Loading from \"@/components/loading\";\nimport { FileEntry } from \"plugin/src/lib\";\nimport { RunningBar } from \"@/components/running-bar\";\nimport { Header } from \"@/components/header\";\nimport { useMDXContent } from \"@/hooks/use-mdx-content\";\nimport { mdxComponents } from \"@/components/mdx-components\";\n\nexport function Post() {\n const { \"*\": rawPath = \".\" } = useParams();\n\n // The path includes the .mdx extension from the route\n const mdxPath = rawPath;\n\n // Extract directory path for finding sibling files (slides, etc.)\n const dirPath = mdxPath.replace(/\\/[^/]+\\.mdx$/, '') || '.';\n\n const { directory, loading: dirLoading } = useDirectory(dirPath)\n const { Content, frontmatter, loading: mdxLoading, error } = useMDXContent(mdxPath);\n const isRunning = isSimulationRunning();\n\n let slides: FileEntry | null = null;\n if (directory) {\n slides = findSlides(directory);\n }\n\n const loading = dirLoading || mdxLoading;\n\n if (loading) return <Loading />\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 return (\n <div className=\"flex min-h-screen flex-col bg-background noise-overlay\">\n <title>{frontmatter?.title}</title>\n <RunningBar />\n <Header />\n <main className=\"flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]\">\n {isRunning && (\n <div className=\"sticky top-0 z-50 px-[var(--page-padding)] py-2 bg-red-500 text-primary-foreground font-mono text-xs text-center tracking-wide\">\n <span className=\"inline-flex items-center gap-3\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current animate-pulse\" />\n <span className=\"uppercase tracking-widest\">simulation running</span>\n <span className=\"text-primary-foreground/60\">Page will auto-refresh on completion</span>\n </span>\n </div>\n )}\n\n {Content && (\n <article className=\"my-24 prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline max-w-[var(--prose-width)] animate-fade-in\">\n <Content components={mdxComponents} />\n </article>\n )}\n </main>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AASO,SAAS,OAAO;AACrB,QAAM,EAAE,KAAK,UAAU,IAAA,IAAQ,UAAA;AAG/B,QAAM,UAAU;AAGhB,QAAM,UAAU,QAAQ,QAAQ,iBAAiB,EAAE,KAAK;AAExD,QAAM,EAAE,UAA+B,IAAI,aAAa,OAAO;AAC/D,QAAM,EAAE,SAAS,aAAa,SAAS,YAAY,MAAA,IAAU,cAAc,OAAO;AAClF,QAAM,YAAY,oBAAA;AAGlB,MAAI,WAAW;AACJ,eAAW,SAAS;AAAA,EAC/B;AAEA,QAAM,UAAwB;AAE9B,MAAI,QAAS,QAAO,oBAAC,SAAA,CAAA,CAAQ;AAE7B,MAAI,OAAO;AACT,WACE,oBAAC,QAAA,EAAK,WAAU,gEACd,UAAA,oBAAC,OAAE,WAAU,4BAA4B,UAAA,MAAM,QAAA,CAAQ,GACzD;AAAA,EAEJ;AAEA,SACE,qBAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAO,qDAAa,MAAA,CAAM;AAAA,wBAC1B,YAAA,EAAW;AAAA,wBACX,QAAA,EAAO;AAAA,IACR,qBAAC,QAAA,EAAK,WAAU,+EACb,UAAA;AAAA,MAAA,iCACE,OAAA,EAAI,WAAU,kIACb,UAAA,qBAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,oDAAA,CAAoD;AAAA,QACpE,oBAAC,QAAA,EAAK,WAAU,6BAA4B,UAAA,sBAAkB;AAAA,QAC9D,oBAAC,QAAA,EAAK,WAAU,8BAA6B,UAAA,uCAAA,CAAoC;AAAA,MAAA,EAAA,CACnF,EAAA,CACF;AAAA,MAGD,+BACE,WAAA,EAAQ,WAAU,oMACjB,UAAA,oBAAC,SAAA,EAAQ,YAAY,cAAA,CAAe,EAAA,CACtC;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -1,45 +1,79 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useRef,
|
|
2
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
3
3
|
import { FULLSCREEN_DATA_ATTR } from "../lib/constants.js";
|
|
4
4
|
import { useParams, useSearchParams } from "react-router-dom";
|
|
5
5
|
import Loading from "../components/loading.js";
|
|
6
|
-
import { FrontMatter } from "../components/front-matter.js";
|
|
7
6
|
import { RunningBar } from "../components/running-bar.js";
|
|
8
7
|
import { Header } from "../components/header.js";
|
|
9
8
|
import { useMDXSlides } from "../hooks/use-mdx-content.js";
|
|
10
|
-
import {
|
|
9
|
+
import { slidesMdxComponents } from "../components/slides-renderer.js";
|
|
11
10
|
function SlidesPage() {
|
|
12
|
-
const { "
|
|
11
|
+
const { "*": rawPath = "." } = useParams();
|
|
13
12
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
14
|
-
const
|
|
15
|
-
const {
|
|
16
|
-
const totalSlides =
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const targetTop = target.getBoundingClientRect().top + window.scrollY;
|
|
28
|
-
window.scrollTo({ top: targetTop, behavior: "smooth" });
|
|
13
|
+
const mdxPath = rawPath;
|
|
14
|
+
const { Content, frontmatter, slideCount, loading, error } = useMDXSlides(mdxPath);
|
|
15
|
+
const totalSlides = (slideCount || 0) + 1;
|
|
16
|
+
const [currentSlide, setCurrentSlide] = useState(0);
|
|
17
|
+
const titleSlideRef = useRef(null);
|
|
18
|
+
const contentRef = useRef(null);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const slideParam = parseInt(searchParams.get("slide") || "0", 10);
|
|
21
|
+
if (slideParam > 0 && contentRef.current) {
|
|
22
|
+
const slideEl = contentRef.current.querySelector(`[data-slide-index="${slideParam - 1}"]`);
|
|
23
|
+
if (slideEl) {
|
|
24
|
+
slideEl.scrollIntoView({ behavior: "auto" });
|
|
25
|
+
}
|
|
29
26
|
}
|
|
30
|
-
}, [
|
|
27
|
+
}, [searchParams, Content]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const observer = new IntersectionObserver(
|
|
30
|
+
(entries) => {
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
if (entry.isIntersecting) {
|
|
33
|
+
const index = entry.target.getAttribute("data-slide-index");
|
|
34
|
+
if (index !== null) {
|
|
35
|
+
const slideNum = index === "title" ? 0 : parseInt(index, 10) + 1;
|
|
36
|
+
setCurrentSlide(slideNum);
|
|
37
|
+
setSearchParams(slideNum > 0 ? { slide: String(slideNum) } : {}, { replace: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{ threshold: 0.5 }
|
|
43
|
+
);
|
|
44
|
+
if (titleSlideRef.current) {
|
|
45
|
+
observer.observe(titleSlideRef.current);
|
|
46
|
+
}
|
|
47
|
+
if (contentRef.current) {
|
|
48
|
+
const slides = contentRef.current.querySelectorAll("[data-slide-index]");
|
|
49
|
+
slides.forEach((slide) => observer.observe(slide));
|
|
50
|
+
}
|
|
51
|
+
return () => observer.disconnect();
|
|
52
|
+
}, [Content, setSearchParams]);
|
|
31
53
|
const goToPrevious = useCallback(() => {
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
const prev = Math.max(0, currentSlide - 1);
|
|
55
|
+
if (prev === 0 && titleSlideRef.current) {
|
|
56
|
+
titleSlideRef.current.scrollIntoView({ behavior: "smooth" });
|
|
57
|
+
} else if (contentRef.current) {
|
|
58
|
+
const slideEl = contentRef.current.querySelector(`[data-slide-index="${prev - 1}"]`);
|
|
59
|
+
slideEl == null ? void 0 : slideEl.scrollIntoView({ behavior: "smooth" });
|
|
60
|
+
}
|
|
61
|
+
}, [currentSlide]);
|
|
34
62
|
const goToNext = useCallback(() => {
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
const next = Math.min(totalSlides - 1, currentSlide + 1);
|
|
64
|
+
if (next === 0 && titleSlideRef.current) {
|
|
65
|
+
titleSlideRef.current.scrollIntoView({ behavior: "smooth" });
|
|
66
|
+
} else if (contentRef.current) {
|
|
67
|
+
const slideEl = contentRef.current.querySelector(`[data-slide-index="${next - 1}"]`);
|
|
68
|
+
slideEl == null ? void 0 : slideEl.scrollIntoView({ behavior: "smooth" });
|
|
69
|
+
}
|
|
70
|
+
}, [currentSlide, totalSlides]);
|
|
37
71
|
useEffect(() => {
|
|
38
72
|
const handleKeyDown = (e) => {
|
|
39
|
-
if (e.key === "ArrowUp" || e.key === "k") {
|
|
73
|
+
if (e.key === "ArrowUp" || e.key === "ArrowLeft" || e.key === "k") {
|
|
40
74
|
e.preventDefault();
|
|
41
75
|
goToPrevious();
|
|
42
|
-
} else if (e.key === "ArrowDown" || e.key === "j") {
|
|
76
|
+
} else if (e.key === "ArrowDown" || e.key === "ArrowRight" || e.key === "j") {
|
|
43
77
|
e.preventDefault();
|
|
44
78
|
goToNext();
|
|
45
79
|
}
|
|
@@ -47,39 +81,13 @@ function SlidesPage() {
|
|
|
47
81
|
window.addEventListener("keydown", handleKeyDown);
|
|
48
82
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
49
83
|
}, [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
84
|
if (loading) {
|
|
77
85
|
return /* @__PURE__ */ jsx(Loading, {});
|
|
78
86
|
}
|
|
79
87
|
if (error) {
|
|
80
88
|
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
89
|
}
|
|
82
|
-
if (
|
|
90
|
+
if (!Content) {
|
|
83
91
|
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
92
|
}
|
|
85
93
|
return /* @__PURE__ */ jsxs("main", { className: "slides-container", children: [
|
|
@@ -96,39 +104,7 @@ function SlidesPage() {
|
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
106
|
),
|
|
99
|
-
/* @__PURE__ */
|
|
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
|
-
] })
|
|
107
|
+
/* @__PURE__ */ jsx("div", { ...{ [FULLSCREEN_DATA_ATTR]: "true" }, children: /* @__PURE__ */ jsx("div", { ref: contentRef, children: /* @__PURE__ */ jsx(Content, { components: slidesMdxComponents }) }) })
|
|
132
108
|
] });
|
|
133
109
|
}
|
|
134
110
|
export {
|
|
@@ -1 +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 {
|
|
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 { RunningBar } from \"@/components/running-bar\";\nimport { Header } from \"@/components/header\";\nimport { useMDXSlides } from \"@/hooks/use-mdx-content\";\nimport { slidesMdxComponents } from \"@/components/slides-renderer\";\n\n\nexport function SlidesPage() {\n const { \"*\": rawPath = \".\" } = useParams();\n const [searchParams, setSearchParams] = useSearchParams();\n\n // The path includes the .mdx extension from the route\n const mdxPath = rawPath;\n\n // Load the compiled MDX module (now includes slideCount export)\n const { Content, frontmatter, slideCount, loading, error } = useMDXSlides(mdxPath);\n\n // Total slides = 1 (title) + content slides\n const totalSlides = (slideCount || 0) + 1;\n\n const [currentSlide, setCurrentSlide] = useState(0);\n const titleSlideRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n\n // Scroll to slide on initial load if query param is set\n useEffect(() => {\n const slideParam = parseInt(searchParams.get(\"slide\") || \"0\", 10);\n if (slideParam > 0 && contentRef.current) {\n const slideEl = contentRef.current.querySelector(`[data-slide-index=\"${slideParam - 1}\"]`);\n if (slideEl) {\n slideEl.scrollIntoView({ behavior: \"auto\" });\n }\n }\n }, [searchParams, Content]);\n\n // Track current slide based on scroll position\n useEffect(() => {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const index = entry.target.getAttribute(\"data-slide-index\");\n if (index !== null) {\n const slideNum = index === \"title\" ? 0 : parseInt(index, 10) + 1;\n setCurrentSlide(slideNum);\n setSearchParams(slideNum > 0 ? { slide: String(slideNum) } : {}, { replace: true });\n }\n }\n }\n },\n { threshold: 0.5 }\n );\n\n // Observe title slide\n if (titleSlideRef.current) {\n observer.observe(titleSlideRef.current);\n }\n\n // Observe content slides\n if (contentRef.current) {\n const slides = contentRef.current.querySelectorAll(\"[data-slide-index]\");\n slides.forEach((slide) => observer.observe(slide));\n }\n\n return () => observer.disconnect();\n }, [Content, setSearchParams]);\n\n // Keyboard/scroll navigation helpers\n const goToPrevious = useCallback(() => {\n const prev = Math.max(0, currentSlide - 1);\n if (prev === 0 && titleSlideRef.current) {\n titleSlideRef.current.scrollIntoView({ behavior: \"smooth\" });\n } else if (contentRef.current) {\n const slideEl = contentRef.current.querySelector(`[data-slide-index=\"${prev - 1}\"]`);\n slideEl?.scrollIntoView({ behavior: \"smooth\" });\n }\n }, [currentSlide]);\n\n const goToNext = useCallback(() => {\n const next = Math.min(totalSlides - 1, currentSlide + 1);\n if (next === 0 && titleSlideRef.current) {\n titleSlideRef.current.scrollIntoView({ behavior: \"smooth\" });\n } else if (contentRef.current) {\n const slideEl = contentRef.current.querySelector(`[data-slide-index=\"${next - 1}\"]`);\n slideEl?.scrollIntoView({ behavior: \"smooth\" });\n }\n }, [currentSlide, totalSlides]);\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\" || e.key === \"k\") {\n e.preventDefault();\n goToPrevious();\n } else if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\" || 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 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 (!Content) {\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 <div ref={contentRef}>\n <Content components={slidesMdxComponents} />\n </div>\n </div>\n </main>\n )\n}\n"],"names":[],"mappings":"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"}
|