veslx 0.1.27 → 0.1.29

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 (50) hide show
  1. package/bin/lib/build.ts +2 -1
  2. package/bin/lib/export.ts +214 -0
  3. package/bin/lib/serve.ts +2 -1
  4. package/dist/client/components/front-matter.js +46 -1
  5. package/dist/client/components/front-matter.js.map +1 -1
  6. package/dist/client/components/header.js +2 -24
  7. package/dist/client/components/header.js.map +1 -1
  8. package/dist/client/components/mdx-components.js +2 -0
  9. package/dist/client/components/mdx-components.js.map +1 -1
  10. package/dist/client/components/post-list-item.js +43 -0
  11. package/dist/client/components/post-list-item.js.map +1 -0
  12. package/dist/client/components/post-list.js +54 -79
  13. package/dist/client/components/post-list.js.map +1 -1
  14. package/dist/client/hooks/use-mdx-content.js +64 -4
  15. package/dist/client/hooks/use-mdx-content.js.map +1 -1
  16. package/dist/client/lib/content-classification.js +1 -22
  17. package/dist/client/lib/content-classification.js.map +1 -1
  18. package/dist/client/pages/content-router.js +2 -10
  19. package/dist/client/pages/content-router.js.map +1 -1
  20. package/dist/client/pages/home.js +20 -23
  21. package/dist/client/pages/home.js.map +1 -1
  22. package/dist/client/pages/index-post.js +34 -0
  23. package/dist/client/pages/index-post.js.map +1 -0
  24. package/dist/client/pages/post.js +1 -3
  25. package/dist/client/pages/post.js.map +1 -1
  26. package/dist/client/pages/slides.js +4 -3
  27. package/dist/client/pages/slides.js.map +1 -1
  28. package/package.json +1 -1
  29. package/plugin/src/plugin.ts +127 -30
  30. package/plugin/src/types.ts +34 -4
  31. package/src/components/front-matter.tsx +60 -3
  32. package/src/components/header.tsx +2 -20
  33. package/src/components/mdx-components.tsx +3 -1
  34. package/src/components/post-list-item.tsx +54 -0
  35. package/src/components/post-list.tsx +74 -116
  36. package/src/components/welcome.tsx +2 -2
  37. package/src/hooks/use-mdx-content.ts +96 -7
  38. package/src/index.css +17 -0
  39. package/src/lib/content-classification.ts +0 -24
  40. package/src/pages/content-router.tsx +6 -17
  41. package/src/pages/home.tsx +26 -58
  42. package/src/pages/index-post.tsx +59 -0
  43. package/src/pages/post.tsx +1 -3
  44. package/src/pages/slides.tsx +5 -3
  45. package/src/vite-env.d.ts +11 -1
  46. package/vite.config.ts +4 -3
  47. package/dist/client/components/running-bar.js +0 -15
  48. package/dist/client/components/running-bar.js.map +0 -1
  49. package/src/components/content-tabs.tsx +0 -64
  50. package/src/components/running-bar.tsx +0 -21
@@ -1,102 +1,77 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Link } from "react-router-dom";
3
- import { cn } from "../lib/utils.js";
4
- import { formatDate } from "../lib/format-date.js";
5
- import { ArrowRight, Presentation } from "lucide-react";
6
- import { directoryToPostEntries, filterVisiblePosts, filterByView, getFrontmatter } from "../lib/content-classification.js";
7
- function extractOrder(name) {
8
- const match = name.match(/^(\d+)-/);
9
- return match ? parseInt(match[1], 10) : null;
10
- }
11
- function stripNumericPrefix(name) {
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useParams } from "react-router-dom";
3
+ import { directoryToPostEntries, filterVisiblePosts, getFrontmatter } from "../lib/content-classification.js";
4
+ import { useDirectory } from "../plugin/src/client.js";
5
+ import { ErrorDisplay } from "./page-error.js";
6
+ import { PostListItem } from "./post-list-item.js";
7
+ import veslxConfig from "virtual:veslx-config";
8
+ function formatName(name) {
12
9
  return name.replace(/^\d+-/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
13
10
  }
14
- function PostList({ directory, view = "all" }) {
11
+ function getLinkPath(post) {
12
+ if (post.file) {
13
+ return `/${post.file.path}`;
14
+ } else if (post.slides && !post.readme) {
15
+ return `/${post.slides.path}`;
16
+ } else if (post.readme) {
17
+ return `/${post.readme.path}`;
18
+ } else {
19
+ return `/${post.path}`;
20
+ }
21
+ }
22
+ function PostList() {
23
+ var _a;
24
+ const { "*": path = "." } = useParams();
25
+ const { directory, error } = useDirectory(path);
26
+ if (error) {
27
+ return /* @__PURE__ */ jsx(ErrorDisplay, { error, path });
28
+ }
29
+ if (!directory) {
30
+ return /* @__PURE__ */ jsx("div", { className: "py-24 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground font-mono text-sm tracking-wide", children: "no entries" }) });
31
+ }
15
32
  let posts = directoryToPostEntries(directory);
16
33
  if (posts.length === 0) {
17
34
  return /* @__PURE__ */ jsx("div", { className: "py-24 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground font-mono text-sm tracking-wide", children: "no entries" }) });
18
35
  }
19
36
  posts = filterVisiblePosts(posts);
20
- posts = filterByView(posts, view);
21
37
  if (posts.length === 0) {
22
38
  return /* @__PURE__ */ jsx("div", { className: "py-24 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground font-mono text-sm tracking-wide", children: "no entries" }) });
23
39
  }
24
- const getPostDate = (post) => {
25
- const frontmatter = getFrontmatter(post);
26
- return (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
27
- };
28
- posts = posts.sort((a, b) => {
29
- var _a, _b;
30
- const aOrder = extractOrder(a.name);
31
- const bOrder = extractOrder(b.name);
32
- const aDate = getPostDate(a);
33
- const bDate = getPostDate(b);
34
- if (aOrder !== null && bOrder !== null) {
35
- return aOrder - bOrder;
36
- }
37
- if (aOrder !== null) return -1;
38
- if (bOrder !== null) return 1;
39
- if (aDate && bDate) {
40
- return bDate.getTime() - aDate.getTime();
41
- }
42
- if (aDate) return -1;
43
- if (bDate) return 1;
44
- const aTitle = ((_a = getFrontmatter(a)) == null ? void 0 : _a.title) || a.name;
45
- const bTitle = ((_b = getFrontmatter(b)) == null ? void 0 : _b.title) || b.name;
46
- return aTitle.localeCompare(bTitle);
47
- });
48
- return /* @__PURE__ */ jsx("div", { className: "space-y-1", children: posts.map((post) => {
40
+ const sortMode = ((_a = veslxConfig.posts) == null ? void 0 : _a.sort) ?? "alpha";
41
+ if (sortMode === "date") {
42
+ posts = posts.sort((a, b) => {
43
+ var _a2, _b;
44
+ const dateA = (_a2 = getFrontmatter(a)) == null ? void 0 : _a2.date;
45
+ const dateB = (_b = getFrontmatter(b)) == null ? void 0 : _b.date;
46
+ if (!dateA && !dateB) return a.name.localeCompare(b.name);
47
+ if (!dateA) return 1;
48
+ if (!dateB) return -1;
49
+ return new Date(dateB).getTime() - new Date(dateA).getTime();
50
+ });
51
+ } else {
52
+ posts = posts.sort((a, b) => a.name.localeCompare(b.name));
53
+ }
54
+ return /* @__PURE__ */ jsx("div", { className: "space-y-1 not-prose", children: posts.map((post) => {
49
55
  const frontmatter = getFrontmatter(post);
50
- const title = (frontmatter == null ? void 0 : frontmatter.title) || stripNumericPrefix(post.name);
56
+ const title = (frontmatter == null ? void 0 : frontmatter.title) || formatName(post.name);
51
57
  const description = frontmatter == null ? void 0 : frontmatter.description;
52
- const date = (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
53
- let linkPath;
54
- if (post.file) {
55
- linkPath = `/${post.file.path}`;
56
- } else if (post.slides && !post.readme) {
57
- linkPath = `/${post.slides.path}`;
58
- } else if (post.readme) {
59
- linkPath = `/${post.readme.path}`;
60
- } else {
61
- linkPath = `/${post.path}`;
62
- }
58
+ const date = (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : void 0;
59
+ const linkPath = getLinkPath(post);
63
60
  const isSlides = linkPath.endsWith("SLIDES.mdx") || linkPath.endsWith(".slides.mdx");
64
61
  return /* @__PURE__ */ jsx(
65
- Link,
62
+ PostListItem,
66
63
  {
67
- to: linkPath,
68
- className: cn(
69
- "group block py-3 px-3 -mx-3 rounded-md",
70
- "transition-colors duration-150"
71
- ),
72
- children: /* @__PURE__ */ jsxs("article", { className: "flex items-center gap-4", children: [
73
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
74
- /* @__PURE__ */ jsxs("h3", { className: cn(
75
- "text-sm font-medium text-foreground",
76
- "group-hover:underline",
77
- "flex items-center gap-2"
78
- ), children: [
79
- /* @__PURE__ */ jsx("span", { children: title }),
80
- /* @__PURE__ */ jsx(ArrowRight, { className: "h-3 w-3 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200 text-primary" })
81
- ] }),
82
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-1 mt-0.5", children: description })
83
- ] }),
84
- isSlides && /* @__PURE__ */ jsx(Presentation, { className: "h-3 w-3 text-muted-foreground" }),
85
- /* @__PURE__ */ jsx(
86
- "time",
87
- {
88
- dateTime: date == null ? void 0 : date.toISOString(),
89
- className: "font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0",
90
- children: date && formatDate(date)
91
- }
92
- )
93
- ] })
64
+ title,
65
+ description,
66
+ date,
67
+ linkPath,
68
+ isSlides
94
69
  },
95
70
  post.path
96
71
  );
97
72
  }) });
98
73
  }
99
74
  export {
100
- PostList as default
75
+ PostList
101
76
  };
102
77
  //# sourceMappingURL=post-list.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"post-list.js","sources":["../../../src/components/post-list.tsx"],"sourcesContent":["import { Link } from \"react-router-dom\";\nimport { cn } from \"@/lib/utils\";\nimport type { DirectoryEntry } from \"../../plugin/src/lib\";\nimport { formatDate } from \"@/lib/format-date\";\nimport { ArrowRight, Presentation } from \"lucide-react\";\nimport {\n type ContentView,\n type PostEntry,\n directoryToPostEntries,\n filterVisiblePosts,\n filterByView,\n getFrontmatter,\n} from \"@/lib/content-classification\";\n\n// Helper to extract numeric prefix from filename (e.g., \"01-intro\" → 1)\nfunction extractOrder(name: string): number | null {\n const match = name.match(/^(\\d+)-/);\n return match ? parseInt(match[1], 10) : null;\n}\n\n// Helper to strip numeric prefix for display (e.g., \"01-getting-started\" → \"Getting Started\")\nfunction stripNumericPrefix(name: string): string {\n return name\n .replace(/^\\d+-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n}\n\ninterface PostListProps {\n directory: DirectoryEntry;\n view?: ContentView;\n}\n\nexport default function PostList({ directory, view = 'all' }: PostListProps) {\n let posts = directoryToPostEntries(directory);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Filter out hidden and draft posts\n posts = filterVisiblePosts(posts);\n\n // Apply view filter\n posts = filterByView(posts, view);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Helper to get date from post\n const getPostDate = (post: PostEntry): Date | null => {\n const frontmatter = getFrontmatter(post);\n return frontmatter?.date ? new Date(frontmatter.date as string) : null;\n };\n\n // Smart sorting: numeric prefix → date → alphabetical\n posts = posts.sort((a, b) => {\n const aOrder = extractOrder(a.name);\n const bOrder = extractOrder(b.name);\n const aDate = getPostDate(a);\n const bDate = getPostDate(b);\n\n // Both have numeric prefix → sort by number\n if (aOrder !== null && bOrder !== null) {\n return aOrder - bOrder;\n }\n // One has prefix, one doesn't → prefixed comes first\n if (aOrder !== null) return -1;\n if (bOrder !== null) return 1;\n\n // Both have dates → sort by date (newest first)\n if (aDate && bDate) {\n return bDate.getTime() - aDate.getTime();\n }\n // One has date → dated comes first\n if (aDate) return -1;\n if (bDate) return 1;\n\n // Neither alphabetical by title\n const aTitle = (getFrontmatter(a)?.title as string) || a.name;\n const bTitle = (getFrontmatter(b)?.title as string) || b.name;\n return aTitle.localeCompare(bTitle);\n });\n\n return (\n <div className=\"space-y-1\">\n {posts.map((post) => {\n const frontmatter = getFrontmatter(post);\n\n // Title: explicit frontmatter > stripped numeric prefix > raw name\n const title = (frontmatter?.title as string) || stripNumericPrefix(post.name);\n const description = frontmatter?.description as string | undefined;\n const date = frontmatter?.date ? new Date(frontmatter.date as string) : null;\n\n // Determine the link path\n let linkPath: string;\n if (post.file) {\n // Standalone MDX file\n linkPath = `/${post.file.path}`;\n } else if (post.slides && !post.readme) {\n // Folder with only slides\n linkPath = `/${post.slides.path}`;\n } else if (post.readme) {\n // Folder with readme\n linkPath = `/${post.readme.path}`;\n } else {\n // Fallback to folder path\n linkPath = `/${post.path}`;\n }\n\n const isSlides = linkPath.endsWith('SLIDES.mdx') || linkPath.endsWith('.slides.mdx');\n\n return (\n <Link\n key={post.path}\n to={linkPath}\n className={cn(\n \"group block py-3 px-3 -mx-3 rounded-md\",\n \"transition-colors duration-150\",\n )}\n >\n <article className=\"flex items-center gap-4\">\n {/* Main content */}\n <div className=\"flex-1 min-w-0\">\n <h3 className={cn(\n \"text-sm font-medium text-foreground\",\n \"group-hover:underline\",\n \"flex items-center gap-2\"\n )}>\n <span>{title}</span>\n <ArrowRight className=\"h-3 w-3 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200 text-primary\" />\n </h3>\n\n {description && (\n <p className=\"text-sm text-muted-foreground line-clamp-1 mt-0.5\">\n {description}\n </p>\n )}\n </div>\n\n {isSlides && (\n <Presentation className=\"h-3 w-3 text-muted-foreground\" />\n )}\n <time\n dateTime={date?.toISOString()}\n className=\"font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0\"\n >\n {date && formatDate(date)}\n </time>\n </article>\n </Link>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"post-list.js","sources":["../../../src/components/post-list.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport {\n type PostEntry,\n directoryToPostEntries,\n filterVisiblePosts,\n getFrontmatter,\n} from \"@/lib/content-classification\";\nimport { useDirectory } from \"../../plugin/src/client\";\nimport { ErrorDisplay } from \"./page-error\";\nimport Loading from \"./loading\";\nimport { PostListItem } from \"./post-list-item\";\nimport veslxConfig from \"virtual:veslx-config\";\n\n// Helper to format name for display (e.g., \"01-getting-started\" → \"Getting Started\")\nfunction formatName(name: string): string {\n return name\n .replace(/^\\d+-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n}\n\n// Helper to get link path from post\nfunction getLinkPath(post: PostEntry): string {\n if (post.file) {\n // Standalone MDX file\n return `/${post.file.path}`;\n } else if (post.slides && !post.readme) {\n // Folder with only slides\n return `/${post.slides.path}`;\n } else if (post.readme) {\n // Folder with readme\n return `/${post.readme.path}`;\n } else {\n // Fallback to folder path\n return `/${post.path}`;\n }\n}\n\nexport function PostList() {\n const { \"*\": path = \".\" } = useParams();\n\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 if (!directory) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n let posts = directoryToPostEntries(directory);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Filter out hidden and draft posts\n posts = filterVisiblePosts(posts);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Sort based on config\n const sortMode = veslxConfig.posts?.sort ?? 'alpha';\n if (sortMode === 'date') {\n // Sort by date descending (newest first), posts without dates go to the end\n posts = posts.sort((a, b) => {\n const dateA = getFrontmatter(a)?.date;\n const dateB = getFrontmatter(b)?.date;\n if (!dateA && !dateB) return a.name.localeCompare(b.name);\n if (!dateA) return 1;\n if (!dateB) return -1;\n return new Date(dateB as string).getTime() - new Date(dateA as string).getTime();\n });\n } else {\n // Alphanumeric sorting by name\n posts = posts.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n return (\n <div className=\"space-y-1 not-prose\">\n {posts.map((post) => {\n const frontmatter = getFrontmatter(post);\n const title = (frontmatter?.title as string) || formatName(post.name);\n const description = frontmatter?.description as string | undefined;\n const date = frontmatter?.date ? new Date(frontmatter.date as string) : undefined;\n const linkPath = getLinkPath(post);\n const isSlides = linkPath.endsWith('SLIDES.mdx') || linkPath.endsWith('.slides.mdx');\n\n return (\n <PostListItem\n key={post.path}\n title={title}\n description={description}\n date={date}\n linkPath={linkPath}\n isSlides={isSlides}\n />\n );\n })}\n </div>\n );\n}\n"],"names":["_a"],"mappings":";;;;;;;AAcA,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,SAAS,EAAE,EACnB,QAAQ,MAAM,GAAG,EACjB,QAAQ,SAAS,CAAA,MAAK,EAAE,aAAa;AAC1C;AAGA,SAAS,YAAY,MAAyB;AAC5C,MAAI,KAAK,MAAM;AAEb,WAAO,IAAI,KAAK,KAAK,IAAI;AAAA,EAC3B,WAAW,KAAK,UAAU,CAAC,KAAK,QAAQ;AAEtC,WAAO,IAAI,KAAK,OAAO,IAAI;AAAA,EAC7B,WAAW,KAAK,QAAQ;AAEtB,WAAO,IAAI,KAAK,OAAO,IAAI;AAAA,EAC7B,OAAO;AAEL,WAAO,IAAI,KAAK,IAAI;AAAA,EACtB;AACF;AAEO,SAAS,WAAW;;AACzB,QAAM,EAAE,KAAK,OAAO,IAAA,IAAQ,UAAA;AAE5B,QAAM,EAAE,WAAoB,UAAU,aAAa,IAAI;AAEvD,MAAI,OAAO;AACT,WAAO,oBAAC,cAAA,EAAa,OAAc,KAAA,CAAY;AAAA,EACjD;AAQA,MAAI,CAAC,WAAW;AACd,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAEA,MAAI,QAAQ,uBAAuB,SAAS;AAE5C,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAGA,UAAQ,mBAAmB,KAAK;AAEhC,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAGA,QAAM,aAAW,iBAAY,UAAZ,mBAAmB,SAAQ;AAC5C,MAAI,aAAa,QAAQ;AAEvB,YAAQ,MAAM,KAAK,CAAC,GAAG,MAAM;;AAC3B,YAAM,SAAQA,MAAA,eAAe,CAAC,MAAhB,gBAAAA,IAAmB;AACjC,YAAM,SAAQ,oBAAe,CAAC,MAAhB,mBAAmB;AACjC,UAAI,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,cAAc,EAAE,IAAI;AACxD,UAAI,CAAC,MAAO,QAAO;AACnB,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO,IAAI,KAAK,KAAe,EAAE,QAAA,IAAY,IAAI,KAAK,KAAe,EAAE,QAAA;AAAA,IACzE,CAAC;AAAA,EACH,OAAO;AAEL,YAAQ,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,EAC3D;AAEA,6BACG,OAAA,EAAI,WAAU,uBACZ,UAAA,MAAM,IAAI,CAAC,SAAS;AACnB,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,SAAS,2CAAa,UAAoB,WAAW,KAAK,IAAI;AACpE,UAAM,cAAc,2CAAa;AACjC,UAAM,QAAO,2CAAa,QAAO,IAAI,KAAK,YAAY,IAAc,IAAI;AACxE,UAAM,WAAW,YAAY,IAAI;AACjC,UAAM,WAAW,SAAS,SAAS,YAAY,KAAK,SAAS,SAAS,aAAa;AAEnF,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MALK,KAAK;AAAA,IAAA;AAAA,EAQhB,CAAC,EAAA,CACH;AAEJ;"}
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
2
2
  function findMdxModule(modules, path) {
3
3
  const keys = Object.keys(modules);
4
4
  const normalizedPath = path.replace(/^\//, "");
5
- if (normalizedPath.endsWith(".mdx")) {
5
+ if (normalizedPath.endsWith(".mdx") || normalizedPath.endsWith(".md")) {
6
6
  const matchingKey = keys.find((key) => {
7
7
  if (key.endsWith(`/${normalizedPath}`)) return true;
8
8
  if (key === `@content/${normalizedPath}`) return true;
@@ -15,8 +15,11 @@ function findMdxModule(modules, path) {
15
15
  }
16
16
  const candidates = [
17
17
  `${normalizedPath}/index.mdx`,
18
+ `${normalizedPath}/index.md`,
18
19
  `${normalizedPath}/README.mdx`,
19
- `${normalizedPath}.mdx`
20
+ `${normalizedPath}/README.md`,
21
+ `${normalizedPath}.mdx`,
22
+ `${normalizedPath}.md`
20
23
  ];
21
24
  for (const candidate of candidates) {
22
25
  const matchingKey = keys.find((key) => {
@@ -67,7 +70,7 @@ function useMDXContent(path) {
67
70
  function findSlidesModule(modules, path) {
68
71
  const keys = Object.keys(modules);
69
72
  const normalizedPath = path.replace(/^\//, "");
70
- if (normalizedPath.endsWith(".mdx")) {
73
+ if (normalizedPath.endsWith(".mdx") || normalizedPath.endsWith(".md")) {
71
74
  const matchingKey = keys.find((key) => {
72
75
  if (key.endsWith(`/${normalizedPath}`)) return true;
73
76
  if (key === `@content/${normalizedPath}`) return true;
@@ -80,7 +83,9 @@ function findSlidesModule(modules, path) {
80
83
  }
81
84
  const candidates = [
82
85
  `${normalizedPath}/SLIDES.mdx`,
83
- `${normalizedPath}/index.slides.mdx`
86
+ `${normalizedPath}/SLIDES.md`,
87
+ `${normalizedPath}/index.slides.mdx`,
88
+ `${normalizedPath}/index.slides.md`
84
89
  ];
85
90
  for (const candidate of candidates) {
86
91
  const matchingKey = keys.find((key) => {
@@ -130,7 +135,62 @@ function useMDXSlides(path) {
130
135
  }, [path]);
131
136
  return { Content, frontmatter, slideCount, loading, error };
132
137
  }
138
+ function findIndexModule(modules, path) {
139
+ const keys = Object.keys(modules);
140
+ const normalizedPath = path.replace(/^\//, "") || ".";
141
+ const candidates = normalizedPath === "." ? ["index.mdx", "index.md"] : [`${normalizedPath}/index.mdx`, `${normalizedPath}/index.md`];
142
+ for (const candidate of candidates) {
143
+ const matchingKey = keys.find((key) => {
144
+ if (key.endsWith(`/${candidate}`)) return true;
145
+ if (key === `@content/${candidate}`) return true;
146
+ if (key === candidate) return true;
147
+ return false;
148
+ });
149
+ if (matchingKey) {
150
+ return modules[matchingKey];
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ function useIndexContent(path) {
156
+ const [Content, setContent] = useState(null);
157
+ const [frontmatter, setFrontmatter] = useState(void 0);
158
+ const [loading, setLoading] = useState(true);
159
+ const [notFound, setNotFound] = useState(false);
160
+ useEffect(() => {
161
+ let cancelled = false;
162
+ setLoading(true);
163
+ setNotFound(false);
164
+ import("virtual:content-modules").then(({ modules }) => {
165
+ const loader = findIndexModule(modules, path);
166
+ if (!loader) {
167
+ if (!cancelled) {
168
+ setNotFound(true);
169
+ setLoading(false);
170
+ }
171
+ return null;
172
+ }
173
+ return loader();
174
+ }).then((mod) => {
175
+ if (mod && !cancelled) {
176
+ setContent(() => mod.default);
177
+ setFrontmatter(mod.frontmatter);
178
+ setLoading(false);
179
+ }
180
+ }).catch(() => {
181
+ if (!cancelled) {
182
+ setNotFound(true);
183
+ setLoading(false);
184
+ }
185
+ });
186
+ return () => {
187
+ cancelled = true;
188
+ };
189
+ }, [path]);
190
+ return { Content, frontmatter, loading, notFound };
191
+ }
133
192
  export {
193
+ useIndexContent,
134
194
  useMDXContent,
135
195
  useMDXSlides
136
196
  };
@@ -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 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 // 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.slides.mdx matches docs/foo.slides.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:\n // 1. folder/SLIDES.mdx (current convention)\n // 2. folder/index.slides.mdx (alternative)\n const candidates = [\n `${normalizedPath}/SLIDES.mdx`,\n `${normalizedPath}/index.slides.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 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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"}
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/MD module by path. Supports:\n * - Full path: \"docs/intro.mdx\" or \"docs/intro.md\" -> matches exactly\n * - Folder path: \"docs\" -> matches \"docs/index.mdx\", \"docs/README.mdx\", or \"docs.mdx\" (and .md variants)\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 or .md, match exactly\n if (normalizedPath.endsWith('.mdx') || normalizedPath.endsWith('.md')) {\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 // Also try .md variants\n const candidates = [\n `${normalizedPath}/index.mdx`,\n `${normalizedPath}/index.md`,\n `${normalizedPath}/README.mdx`,\n `${normalizedPath}/README.md`,\n `${normalizedPath}.mdx`,\n `${normalizedPath}.md`,\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 // Normalize path - remove leading slash if present\n const normalizedPath = path.replace(/^\\//, '')\n\n // If path already ends with .mdx or .md, match exactly\n if (normalizedPath.endsWith('.mdx') || normalizedPath.endsWith('.md')) {\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.slides.mdx matches docs/foo.slides.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:\n // 1. folder/SLIDES.mdx (current convention)\n // 2. folder/index.slides.mdx (alternative)\n // Also try .md variants\n const candidates = [\n `${normalizedPath}/SLIDES.mdx`,\n `${normalizedPath}/SLIDES.md`,\n `${normalizedPath}/index.slides.mdx`,\n `${normalizedPath}/index.slides.md`,\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 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\n/**\n * Find only index.mdx or index.md in a directory (not README or other conventions)\n */\nfunction findIndexModule(modules: ModuleMap, path: string): ModuleLoader | null {\n const keys = Object.keys(modules)\n\n // Normalize path - remove leading slash, handle root\n const normalizedPath = path.replace(/^\\//, '') || '.'\n\n // Only look for index.mdx or index.md\n const candidates = normalizedPath === '.'\n ? ['index.mdx', 'index.md']\n : [`${normalizedPath}/index.mdx`, `${normalizedPath}/index.md`]\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\n/**\n * Hook for loading index.mdx/index.md content only.\n * Returns notFound: true if no index file exists (instead of throwing an error).\n */\nexport function useIndexContent(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 [notFound, setNotFound] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setNotFound(false)\n\n import('virtual:content-modules')\n .then(({ modules }) => {\n const loader = findIndexModule(modules as ModuleMap, path)\n\n if (!loader) {\n // No index file - this is not an error, just means fallback to directory listing\n if (!cancelled) {\n setNotFound(true)\n setLoading(false)\n }\n return null\n }\n\n return loader()\n })\n .then((mod) => {\n if (mod && !cancelled) {\n setContent(() => mod.default)\n setFrontmatter(mod.frontmatter)\n setLoading(false)\n }\n })\n .catch(() => {\n // Treat load errors as not found\n if (!cancelled) {\n setNotFound(true)\n setLoading(false)\n }\n })\n\n return () => {\n cancelled = true\n }\n }, [path])\n\n return { Content, frontmatter, loading, notFound }\n}\n"],"names":[],"mappings":"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"}
@@ -3,24 +3,6 @@ function getFrontmatter(post) {
3
3
  var _a, _b, _c;
4
4
  return ((_a = post.readme) == null ? void 0 : _a.frontmatter) || ((_b = post.file) == null ? void 0 : _b.frontmatter) || ((_c = post.slides) == null ? void 0 : _c.frontmatter);
5
5
  }
6
- function hasDate(post) {
7
- const frontmatter = getFrontmatter(post);
8
- return (frontmatter == null ? void 0 : frontmatter.date) !== void 0 && frontmatter.date !== null && frontmatter.date !== "";
9
- }
10
- function filterByView(posts, view) {
11
- if (view === "all") return posts;
12
- if (view === "posts") return posts.filter(hasDate);
13
- if (view === "docs") return posts.filter((post) => !hasDate(post));
14
- return posts;
15
- }
16
- function getViewCounts(posts) {
17
- const dated = posts.filter(hasDate).length;
18
- return {
19
- posts: dated,
20
- docs: posts.length - dated,
21
- all: posts.length
22
- };
23
- }
24
6
  function directoryToPostEntries(directory) {
25
7
  const folders = directory.children.filter((c) => c.type === "directory");
26
8
  const standaloneFiles = findMdxFiles(directory);
@@ -59,10 +41,7 @@ function filterVisiblePosts(posts) {
59
41
  }
60
42
  export {
61
43
  directoryToPostEntries,
62
- filterByView,
63
44
  filterVisiblePosts,
64
- getFrontmatter,
65
- getViewCounts,
66
- hasDate
45
+ getFrontmatter
67
46
  };
68
47
  //# sourceMappingURL=content-classification.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-classification.js","sources":["../../../src/lib/content-classification.ts"],"sourcesContent":["import type { ContentView } from \"../../plugin/src/types\";\nimport type { DirectoryEntry, FileEntry } from \"../../plugin/src/lib\";\nimport { findReadme, findSlides, findMdxFiles, findStandaloneSlides } from \"../../plugin/src/client\";\n\nexport type PostEntry = {\n type: 'folder' | 'file';\n name: string;\n path: string;\n readme: FileEntry | null;\n slides: FileEntry | null;\n file: FileEntry | null;\n};\n\nexport function getFrontmatter(post: PostEntry) {\n return post.readme?.frontmatter || post.file?.frontmatter || post.slides?.frontmatter;\n}\n\nexport function hasDate(post: PostEntry): boolean {\n const frontmatter = getFrontmatter(post);\n return frontmatter?.date !== undefined && frontmatter.date !== null && frontmatter.date !== '';\n}\n\nexport function filterByView(posts: PostEntry[], view: ContentView): PostEntry[] {\n if (view === 'all') return posts;\n if (view === 'posts') return posts.filter(hasDate);\n if (view === 'docs') return posts.filter(post => !hasDate(post));\n return posts;\n}\n\nexport function getViewCounts(posts: PostEntry[]): { posts: number; docs: number; all: number } {\n const dated = posts.filter(hasDate).length;\n return {\n posts: dated,\n docs: posts.length - dated,\n all: posts.length,\n };\n}\n\nexport function directoryToPostEntries(directory: DirectoryEntry): PostEntry[] {\n const folders = directory.children.filter((c): c is DirectoryEntry => c.type === \"directory\");\n const standaloneFiles = findMdxFiles(directory);\n const standaloneSlidesFiles = findStandaloneSlides(directory);\n\n const folderPosts: PostEntry[] = folders\n .map((folder) => ({\n type: 'folder' as const,\n name: folder.name,\n path: folder.path,\n readme: findReadme(folder),\n slides: findSlides(folder),\n file: null,\n }))\n .filter((post) => post.readme || post.slides); // Only include folders with content\n\n const filePosts: PostEntry[] = standaloneFiles.map((file) => ({\n type: 'file' as const,\n name: file.name.replace(/\\.mdx?$/, ''),\n path: file.path,\n readme: null,\n slides: null,\n file,\n }));\n\n // Standalone slides files (e.g., getting-started.slides.mdx)\n const slidesPosts: PostEntry[] = standaloneSlidesFiles.map((file) => ({\n type: 'file' as const,\n name: file.name.replace(/\\.slides\\.mdx?$/, ''),\n path: file.path,\n readme: null,\n slides: file,\n file: null,\n }));\n\n return [...folderPosts, ...filePosts, ...slidesPosts];\n}\n\nexport function filterVisiblePosts(posts: PostEntry[]): PostEntry[] {\n return posts.filter((post) => {\n const frontmatter = getFrontmatter(post);\n return frontmatter?.visibility !== \"hidden\" && frontmatter?.draft !== true;\n });\n}\n\nexport type { ContentView };\n"],"names":[],"mappings":";AAaO,SAAS,eAAe,MAAiB;;AAC9C,WAAO,UAAK,WAAL,mBAAa,kBAAe,UAAK,SAAL,mBAAW,kBAAe,UAAK,WAAL,mBAAa;AAC5E;AAEO,SAAS,QAAQ,MAA0B;AAChD,QAAM,cAAc,eAAe,IAAI;AACvC,UAAO,2CAAa,UAAS,UAAa,YAAY,SAAS,QAAQ,YAAY,SAAS;AAC9F;AAEO,SAAS,aAAa,OAAoB,MAAgC;AAC/E,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,SAAS,QAAS,QAAO,MAAM,OAAO,OAAO;AACjD,MAAI,SAAS,OAAQ,QAAO,MAAM,OAAO,CAAA,SAAQ,CAAC,QAAQ,IAAI,CAAC;AAC/D,SAAO;AACT;AAEO,SAAS,cAAc,OAAkE;AAC9F,QAAM,QAAQ,MAAM,OAAO,OAAO,EAAE;AACpC,SAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM,MAAM,SAAS;AAAA,IACrB,KAAK,MAAM;AAAA,EAAA;AAEf;AAEO,SAAS,uBAAuB,WAAwC;AAC7E,QAAM,UAAU,UAAU,SAAS,OAAO,CAAC,MAA2B,EAAE,SAAS,WAAW;AAC5F,QAAM,kBAAkB,aAAa,SAAS;AAC9C,QAAM,wBAAwB,qBAAqB,SAAS;AAE5D,QAAM,cAA2B,QAC9B,IAAI,CAAC,YAAY;AAAA,IAChB,MAAM;AAAA,IACN,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,QAAQ,WAAW,MAAM;AAAA,IACzB,QAAQ,WAAW,MAAM;AAAA,IACzB,MAAM;AAAA,EAAA,EACN,EACD,OAAO,CAAC,SAAS,KAAK,UAAU,KAAK,MAAM;AAE9C,QAAM,YAAyB,gBAAgB,IAAI,CAAC,UAAU;AAAA,IAC5D,MAAM;AAAA,IACN,MAAM,KAAK,KAAK,QAAQ,WAAW,EAAE;AAAA,IACrC,MAAM,KAAK;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,EAAA,EACA;AAGF,QAAM,cAA2B,sBAAsB,IAAI,CAAC,UAAU;AAAA,IACpE,MAAM;AAAA,IACN,MAAM,KAAK,KAAK,QAAQ,mBAAmB,EAAE;AAAA,IAC7C,MAAM,KAAK;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,EAAA,EACN;AAEF,SAAO,CAAC,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW;AACtD;AAEO,SAAS,mBAAmB,OAAiC;AAClE,SAAO,MAAM,OAAO,CAAC,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,YAAO,2CAAa,gBAAe,aAAY,2CAAa,WAAU;AAAA,EACxE,CAAC;AACH;"}
1
+ {"version":3,"file":"content-classification.js","sources":["../../../src/lib/content-classification.ts"],"sourcesContent":["import type { DirectoryEntry, FileEntry } from \"../../plugin/src/lib\";\nimport { findReadme, findSlides, findMdxFiles, findStandaloneSlides } from \"../../plugin/src/client\";\n\nexport type PostEntry = {\n type: 'folder' | 'file';\n name: string;\n path: string;\n readme: FileEntry | null;\n slides: FileEntry | null;\n file: FileEntry | null;\n};\n\nexport function getFrontmatter(post: PostEntry) {\n return post.readme?.frontmatter || post.file?.frontmatter || post.slides?.frontmatter;\n}\n\nexport function directoryToPostEntries(directory: DirectoryEntry): PostEntry[] {\n const folders = directory.children.filter((c): c is DirectoryEntry => c.type === \"directory\");\n const standaloneFiles = findMdxFiles(directory);\n const standaloneSlidesFiles = findStandaloneSlides(directory);\n\n const folderPosts: PostEntry[] = folders\n .map((folder) => ({\n type: 'folder' as const,\n name: folder.name,\n path: folder.path,\n readme: findReadme(folder),\n slides: findSlides(folder),\n file: null,\n }))\n .filter((post) => post.readme || post.slides); // Only include folders with content\n\n const filePosts: PostEntry[] = standaloneFiles.map((file) => ({\n type: 'file' as const,\n name: file.name.replace(/\\.mdx?$/, ''),\n path: file.path,\n readme: null,\n slides: null,\n file,\n }));\n\n // Standalone slides files (e.g., getting-started.slides.mdx)\n const slidesPosts: PostEntry[] = standaloneSlidesFiles.map((file) => ({\n type: 'file' as const,\n name: file.name.replace(/\\.slides\\.mdx?$/, ''),\n path: file.path,\n readme: null,\n slides: file,\n file: null,\n }));\n\n return [...folderPosts, ...filePosts, ...slidesPosts];\n}\n\nexport function filterVisiblePosts(posts: PostEntry[]): PostEntry[] {\n return posts.filter((post) => {\n const frontmatter = getFrontmatter(post);\n return frontmatter?.visibility !== \"hidden\" && frontmatter?.draft !== true;\n });\n}\n"],"names":[],"mappings":";AAYO,SAAS,eAAe,MAAiB;;AAC9C,WAAO,UAAK,WAAL,mBAAa,kBAAe,UAAK,SAAL,mBAAW,kBAAe,UAAK,WAAL,mBAAa;AAC5E;AAEO,SAAS,uBAAuB,WAAwC;AAC7E,QAAM,UAAU,UAAU,SAAS,OAAO,CAAC,MAA2B,EAAE,SAAS,WAAW;AAC5F,QAAM,kBAAkB,aAAa,SAAS;AAC9C,QAAM,wBAAwB,qBAAqB,SAAS;AAE5D,QAAM,cAA2B,QAC9B,IAAI,CAAC,YAAY;AAAA,IAChB,MAAM;AAAA,IACN,MAAM,OAAO;AAAA,IACb,MAAM,OAAO;AAAA,IACb,QAAQ,WAAW,MAAM;AAAA,IACzB,QAAQ,WAAW,MAAM;AAAA,IACzB,MAAM;AAAA,EAAA,EACN,EACD,OAAO,CAAC,SAAS,KAAK,UAAU,KAAK,MAAM;AAE9C,QAAM,YAAyB,gBAAgB,IAAI,CAAC,UAAU;AAAA,IAC5D,MAAM;AAAA,IACN,MAAM,KAAK,KAAK,QAAQ,WAAW,EAAE;AAAA,IACrC,MAAM,KAAK;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,EAAA,EACA;AAGF,QAAM,cAA2B,sBAAsB,IAAI,CAAC,UAAU;AAAA,IACpE,MAAM;AAAA,IACN,MAAM,KAAK,KAAK,QAAQ,mBAAmB,EAAE;AAAA,IAC7C,MAAM,KAAK;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,EAAA,EACN;AAEF,SAAO,CAAC,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW;AACtD;AAEO,SAAS,mBAAmB,OAAiC;AAClE,SAAO,MAAM,OAAO,CAAC,SAAS;AAC5B,UAAM,cAAc,eAAe,IAAI;AACvC,YAAO,2CAAa,gBAAe,aAAY,2CAAa,WAAU;AAAA,EACxE,CAAC;AACH;"}
@@ -3,18 +3,10 @@ import { useParams } from "react-router-dom";
3
3
  import { Home } from "./home.js";
4
4
  import { Post } from "./post.js";
5
5
  import { SlidesPage } from "./slides.js";
6
+ import { IndexPost } from "./index-post.js";
6
7
  function ContentRouter() {
7
8
  var _a;
8
9
  const { "*": path = "" } = useParams();
9
- if (path === "posts") {
10
- return /* @__PURE__ */ jsx(Home, { view: "posts" });
11
- }
12
- if (path === "docs") {
13
- return /* @__PURE__ */ jsx(Home, { view: "docs" });
14
- }
15
- if (path === "all") {
16
- return /* @__PURE__ */ jsx(Home, { view: "all" });
17
- }
18
10
  const filename = ((_a = path.split("/").pop()) == null ? void 0 : _a.toLowerCase()) || "";
19
11
  const isSlides = path.endsWith(".slides.mdx") || path.endsWith(".slides.md") || filename === "slides.mdx" || filename === "slides.md";
20
12
  if (isSlides) {
@@ -23,7 +15,7 @@ function ContentRouter() {
23
15
  if (path.endsWith(".mdx") || path.endsWith(".md")) {
24
16
  return /* @__PURE__ */ jsx(Post, {});
25
17
  }
26
- return /* @__PURE__ */ jsx(Home, {});
18
+ return /* @__PURE__ */ jsx(IndexPost, { fallback: /* @__PURE__ */ jsx(Home, {}) });
27
19
  }
28
20
  export {
29
21
  ContentRouter
@@ -1 +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 * - /posts Home with posts view\n * - /docs Home with docs view\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 for content view routes\n if (path === 'posts') {\n return <Home view=\"posts\" />\n }\n if (path === 'docs') {\n return <Home view=\"docs\" />\n }\n if (path === 'all') {\n return <Home view=\"all\" />\n }\n\n // Check if this is a slides file\n const filename = path.split('/').pop()?.toLowerCase() || ''\n const isSlides =\n path.endsWith('.slides.mdx') ||\n path.endsWith('.slides.md') ||\n filename === 'slides.mdx' ||\n filename === 'slides.md'\n if (isSlides) {\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":";;;;;AAaO,SAAS,gBAAgB;;AAC9B,QAAM,EAAE,KAAK,OAAO,GAAA,IAAO,UAAA;AAG3B,MAAI,SAAS,SAAS;AACpB,WAAO,oBAAC,MAAA,EAAK,MAAK,QAAA,CAAQ;AAAA,EAC5B;AACA,MAAI,SAAS,QAAQ;AACnB,WAAO,oBAAC,MAAA,EAAK,MAAK,OAAA,CAAO;AAAA,EAC3B;AACA,MAAI,SAAS,OAAO;AAClB,WAAO,oBAAC,MAAA,EAAK,MAAK,MAAA,CAAM;AAAA,EAC1B;AAGA,QAAM,aAAW,UAAK,MAAM,GAAG,EAAE,IAAA,MAAhB,mBAAuB,kBAAiB;AACzD,QAAM,WACJ,KAAK,SAAS,aAAa,KAC3B,KAAK,SAAS,YAAY,KAC1B,aAAa,gBACb,aAAa;AACf,MAAI,UAAU;AACZ,+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
+ {"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\"\nimport { IndexPost } from \"./index-post\"\n\n/**\n * Routes to the appropriate page based on the URL path:\n * - *.slides.mdx or *SLIDES.mdx SlidesPage\n * - *.mdx or *.md Post\n * - directory with index.mdx/index.mdIndexPost (renders index file)\n * - everything else → Home (directory listing)\n */\nexport function ContentRouter() {\n const { \"*\": path = \"\" } = useParams()\n\n // Check if this is a slides file\n const filename = path.split('/').pop()?.toLowerCase() || ''\n const isSlides =\n path.endsWith('.slides.mdx') ||\n path.endsWith('.slides.md') ||\n filename === 'slides.mdx' ||\n filename === 'slides.md'\n if (isSlides) {\n return <SlidesPage />\n }\n\n // Check if this is any MDX/MD file\n if (path.endsWith('.mdx') || path.endsWith('.md')) {\n return <Post />\n }\n\n // For directories, try to render index.mdx/index.md, fallback to Home\n return <IndexPost fallback={<Home />} />\n}\n"],"names":[],"mappings":";;;;;;AAaO,SAAS,gBAAgB;;AAC9B,QAAM,EAAE,KAAK,OAAO,GAAA,IAAO,UAAA;AAG3B,QAAM,aAAW,UAAK,MAAM,GAAG,EAAE,IAAA,MAAhB,mBAAuB,kBAAiB;AACzD,QAAM,WACJ,KAAK,SAAS,aAAa,KAC3B,KAAK,SAAS,YAAY,KAC1B,aAAa,gBACb,aAAa;AACf,MAAI,UAAU;AACZ,+BAAQ,YAAA,EAAW;AAAA,EACrB;AAGA,MAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,KAAK,GAAG;AACjD,+BAAQ,MAAA,EAAK;AAAA,EACf;AAGA,SAAO,oBAAC,WAAA,EAAU,UAAU,oBAAC,QAAK,GAAI;AACxC;"}
@@ -1,35 +1,32 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useParams } from "react-router-dom";
3
- import { useDirectory } from "../plugin/src/client.js";
4
- import PostList from "../components/post-list.js";
5
- import { ErrorDisplay } from "../components/page-error.js";
6
- import { RunningBar } from "../components/running-bar.js";
3
+ import { PostList } from "../components/post-list.js";
7
4
  import { Header } from "../components/header.js";
8
- import { getViewCounts, filterVisiblePosts, directoryToPostEntries } from "../lib/content-classification.js";
9
- import siteConfig from "virtual:veslx-config";
10
- function Home({ view }) {
5
+ import veslxConfig from "virtual:veslx-config";
6
+ function Home() {
11
7
  const { "*": path = "." } = useParams();
12
- const config = siteConfig;
13
- const isViewRoute = path === "posts" || path === "docs" || path === "all";
14
- const directoryPath = isViewRoute ? "." : path;
15
- const { directory, error } = useDirectory(directoryPath);
16
- const activeView = view ?? config.defaultView;
17
- const isRoot = path === "." || path === "" || isViewRoute;
18
- directory ? getViewCounts(filterVisiblePosts(directoryToPostEntries(directory))) : {};
19
- if (error) {
20
- return /* @__PURE__ */ jsx(ErrorDisplay, { error, path });
21
- }
8
+ const config = veslxConfig.site;
9
+ const isRoot = path === "." || path === "";
22
10
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen flex-col bg-background noise-overlay", children: [
23
- /* @__PURE__ */ jsx(RunningBar, {}),
24
11
  /* @__PURE__ */ jsx(Header, {}),
25
12
  /* @__PURE__ */ jsxs("main", { className: "flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]", children: [
26
13
  /* @__PURE__ */ jsx("title", { children: isRoot ? config.name : `${config.name} - ${path}` }),
27
14
  /* @__PURE__ */ jsxs("main", { className: "flex flex-col gap-8 mb-32 mt-12", children: [
28
- isRoot && /* @__PURE__ */ jsxs("div", { className: "animate-fade-in", children: [
29
- /* @__PURE__ */ jsx("h1", { className: "text-2xl md:text-3xl font-semibold tracking-tight text-foreground", children: config.name }),
30
- config.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: config.description })
15
+ isRoot && /* @__PURE__ */ jsxs("div", { className: "animate-fade-in flex items-start justify-between gap-4", children: [
16
+ /* @__PURE__ */ jsxs("div", { children: [
17
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl md:text-3xl font-semibold tracking-tight text-foreground", children: config.name }),
18
+ config.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: config.description })
19
+ ] }),
20
+ config.llmsTxt && /* @__PURE__ */ jsx(
21
+ "a",
22
+ {
23
+ href: "/llms-full.txt",
24
+ className: "font-mono text-xs text-muted-foreground/70 hover:text-foreground underline underline-offset-2 transition-colors shrink-0",
25
+ children: "llms.txt"
26
+ }
27
+ )
31
28
  ] }),
32
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: directory && /* @__PURE__ */ jsx("div", { className: "animate-fade-in", children: /* @__PURE__ */ jsx(PostList, { directory, view: isRoot ? activeView : "all" }) }) })
29
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsx("div", { className: "animate-fade-in", children: /* @__PURE__ */ jsx(PostList, {}) }) })
33
30
  ] })
34
31
  ] })
35
32
  ] });
@@ -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\";\nimport { ContentTabs } from \"@/components/content-tabs\";\nimport {\n type ContentView,\n directoryToPostEntries,\n filterVisiblePosts,\n getViewCounts,\n} from \"@/lib/content-classification\";\nimport siteConfig from \"virtual:veslx-config\";\n\ninterface HomeProps {\n view?: ContentView;\n}\n\nexport function Home({ view }: HomeProps) {\n const { \"*\": path = \".\" } = useParams();\n const config = siteConfig;\n\n // Normalize path - \"posts\", \"docs\", and \"all\" are view routes, not directories\n const isViewRoute = path === \"posts\" || path === \"docs\" || path === \"all\";\n const directoryPath = isViewRoute ? \".\" : path;\n\n const { directory, loading, error } = useDirectory(directoryPath)\n\n // Use prop view, fallback to config default\n const activeView = view ?? config.defaultView;\n\n const isRoot = path === \".\" || path === \"\" || isViewRoute;\n\n // Calculate counts for tabs (only meaningful on root)\n const counts = directory\n ? getViewCounts(filterVisiblePosts(directoryToPostEntries(directory)))\n : { posts: 0, docs: 0, all: 0 };\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>{isRoot ? config.name : `${config.name} - ${path}`}</title>\n <main className=\"flex flex-col gap-8 mb-32 mt-12\">\n {isRoot && (\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\n <div className=\"flex flex-col gap-2\">\n {directory && (\n <div className=\"animate-fade-in\">\n <PostList directory={directory} view={isRoot ? activeView : 'all'} />\n </div>\n )}\n </div>\n </main>\n </main>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;AAoBO,SAAS,KAAK,EAAE,QAAmB;AACxC,QAAM,EAAE,KAAK,OAAO,IAAA,IAAQ,UAAA;AAC5B,QAAM,SAAS;AAGf,QAAM,cAAc,SAAS,WAAW,SAAS,UAAU,SAAS;AACpE,QAAM,gBAAgB,cAAc,MAAM;AAE1C,QAAM,EAAE,WAAoB,UAAU,aAAa,aAAa;AAGhE,QAAM,aAAa,QAAQ,OAAO;AAElC,QAAM,SAAS,SAAS,OAAO,SAAS,MAAM;AAG/B,cACX,cAAc,mBAAmB,uBAAuB,SAAS,CAAC,CAAC,IACnE,CAA4B;AAEhC,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,EAAO,mBAAS,OAAO,OAAO,GAAG,OAAO,IAAI,MAAM,IAAI,GAAA,CAAG;AAAA,MAC1D,qBAAC,QAAA,EAAK,WAAU,mCACb,UAAA;AAAA,QAAA,UACC,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,4BAGD,OAAA,EAAI,WAAU,uBACZ,UAAA,iCACE,OAAA,EAAI,WAAU,mBACb,UAAA,oBAAC,YAAS,WAAsB,MAAM,SAAS,aAAa,OAAO,GACrE,EAAA,CAEJ;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"home.js","sources":["../../../src/pages/home.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\"\nimport { PostList } from \"@/components/post-list\";\nimport { Header } from \"@/components/header\";\nimport veslxConfig from \"virtual:veslx-config\";\n\nexport function Home() {\n const { \"*\": path = \".\" } = useParams();\n const config = veslxConfig.site;\n\n const isRoot = path === \".\" || path === \"\";\n\n return (\n <div className=\"flex min-h-screen flex-col bg-background noise-overlay\">\n <Header />\n <main className=\"flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]\">\n <title>{isRoot ? config.name : `${config.name} - ${path}`}</title>\n <main className=\"flex flex-col gap-8 mb-32 mt-12\">\n {isRoot && (\n <div className=\"animate-fade-in flex items-start justify-between gap-4\">\n <div>\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 {config.llmsTxt && (\n <a\n href=\"/llms-full.txt\"\n className=\"font-mono text-xs text-muted-foreground/70 hover:text-foreground underline underline-offset-2 transition-colors shrink-0\"\n >\n llms.txt\n </a>\n )}\n </div>\n )}\n\n <div className=\"flex flex-col gap-2\">\n <div className=\"animate-fade-in\">\n <PostList />\n </div>\n </div>\n </main>\n </main>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;AAKO,SAAS,OAAO;AACrB,QAAM,EAAE,KAAK,OAAO,IAAA,IAAQ,UAAA;AAC5B,QAAM,SAAS,YAAY;AAE3B,QAAM,SAAS,SAAS,OAAO,SAAS;AAExC,SACE,qBAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAO;AAAA,IACR,qBAAC,QAAA,EAAK,WAAU,+EACd,UAAA;AAAA,MAAA,oBAAC,SAAA,EAAO,mBAAS,OAAO,OAAO,GAAG,OAAO,IAAI,MAAM,IAAI,GAAA,CAAG;AAAA,MAC1D,qBAAC,QAAA,EAAK,WAAU,mCACb,UAAA;AAAA,QAAA,UACC,qBAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EACC,UAAA;AAAA,YAAA,oBAAC,MAAA,EAAG,WAAU,qEACX,UAAA,OAAO,MACV;AAAA,YACC,OAAO,eACN,oBAAC,OAAE,WAAU,8BACV,iBAAO,YAAA,CACV;AAAA,UAAA,GAEJ;AAAA,UACC,OAAO,WACN;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QAED,GAEJ;AAAA,QAGF,oBAAC,OAAA,EAAI,WAAU,uBACb,UAAA,oBAAC,OAAA,EAAI,WAAU,mBACb,UAAA,oBAAC,UAAA,CAAA,CAAS,EAAA,CACZ,EAAA,CACF;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
@@ -0,0 +1,34 @@
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
+ import { useParams } from "react-router-dom";
3
+ import { isSimulationRunning } from "../plugin/src/client.js";
4
+ import Loading from "../components/loading.js";
5
+ import { Header } from "../components/header.js";
6
+ import { useIndexContent } from "../hooks/use-mdx-content.js";
7
+ import { mdxComponents } from "../components/mdx-components.js";
8
+ import { FrontmatterProvider } from "../lib/frontmatter-context.js";
9
+ function IndexPost({ fallback }) {
10
+ const { "*": rawPath = "." } = useParams();
11
+ const dirPath = rawPath || ".";
12
+ const { Content, frontmatter, loading, notFound } = useIndexContent(dirPath);
13
+ const isRunning = isSimulationRunning();
14
+ if (loading) return /* @__PURE__ */ jsx(Loading, {});
15
+ if (notFound) {
16
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
17
+ }
18
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen flex-col bg-background noise-overlay", children: [
19
+ /* @__PURE__ */ jsx("title", { children: frontmatter == null ? void 0 : frontmatter.title }),
20
+ /* @__PURE__ */ jsx(Header, {}),
21
+ /* @__PURE__ */ jsxs("main", { className: "flex-1 w-full overflow-x-clip", children: [
22
+ isRunning && /* @__PURE__ */ jsx("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", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-3", children: [
23
+ /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current animate-pulse" }),
24
+ /* @__PURE__ */ jsx("span", { className: "uppercase tracking-widest", children: "simulation running" }),
25
+ /* @__PURE__ */ jsx("span", { className: "text-primary-foreground/60", children: "Page will auto-refresh on completion" })
26
+ ] }) }),
27
+ Content && /* @__PURE__ */ jsx(FrontmatterProvider, { frontmatter, children: /* @__PURE__ */ jsx("article", { className: "my-12 mx-auto px-[var(--page-padding)] max-w-[var(--content-width)] animate-fade-in", children: /* @__PURE__ */ jsx(Content, { components: mdxComponents }) }) })
28
+ ] })
29
+ ] });
30
+ }
31
+ export {
32
+ IndexPost
33
+ };
34
+ //# sourceMappingURL=index-post.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-post.js","sources":["../../../src/pages/index-post.tsx"],"sourcesContent":["import { ReactNode } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { isSimulationRunning } from \"../../plugin/src/client\";\nimport Loading from \"@/components/loading\";\nimport { Header } from \"@/components/header\";\nimport { useIndexContent } from \"@/hooks/use-mdx-content\";\nimport { mdxComponents } from \"@/components/mdx-components\";\nimport { FrontmatterProvider } from \"@/lib/frontmatter-context\";\n\ninterface IndexPostProps {\n fallback: ReactNode;\n}\n\n/**\n * Attempts to render an index.mdx or index.md file for a directory.\n * Falls back to the provided component if no index file exists.\n */\nexport function IndexPost({ fallback }: IndexPostProps) {\n const { \"*\": rawPath = \".\" } = useParams();\n\n // Normalize path for index lookup\n const dirPath = rawPath || \".\";\n\n const { Content, frontmatter, loading, notFound } = useIndexContent(dirPath);\n const isRunning = isSimulationRunning();\n\n if (loading) return <Loading />\n\n // No index file found - render fallback (usually Home)\n if (notFound) {\n return <>{fallback}</>;\n }\n\n return (\n <div className=\"flex min-h-screen flex-col bg-background noise-overlay\">\n <title>{frontmatter?.title}</title>\n <Header />\n <main className=\"flex-1 w-full overflow-x-clip\">\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 <FrontmatterProvider frontmatter={frontmatter}>\n <article className=\"my-12 mx-auto px-[var(--page-padding)] max-w-[var(--content-width)] animate-fade-in\">\n <Content components={mdxComponents} />\n </article>\n </FrontmatterProvider>\n )}\n </main>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;AAiBO,SAAS,UAAU,EAAE,YAA4B;AACtD,QAAM,EAAE,KAAK,UAAU,IAAA,IAAQ,UAAA;AAG/B,QAAM,UAAU,WAAW;AAE3B,QAAM,EAAE,SAAS,aAAa,SAAS,SAAA,IAAa,gBAAgB,OAAO;AAC3E,QAAM,YAAY,oBAAA;AAElB,MAAI,QAAS,QAAO,oBAAC,SAAA,CAAA,CAAQ;AAG7B,MAAI,UAAU;AACZ,2CAAU,UAAA,SAAA,CAAS;AAAA,EACrB;AAEA,SACE,qBAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAO,qDAAa,MAAA,CAAM;AAAA,wBAC1B,QAAA,EAAO;AAAA,IACR,qBAAC,QAAA,EAAK,WAAU,iCACb,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,WACC,oBAAC,qBAAA,EAAoB,aACnB,UAAA,oBAAC,WAAA,EAAQ,WAAU,uFACjB,UAAA,oBAAC,SAAA,EAAQ,YAAY,cAAA,CAAe,GACtC,EAAA,CACF;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;"}