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.
Files changed (82) hide show
  1. package/README.md +262 -55
  2. package/bin/lib/build.ts +65 -13
  3. package/bin/lib/import-config.ts +10 -9
  4. package/bin/lib/init.ts +21 -22
  5. package/bin/lib/serve.ts +66 -12
  6. package/bin/veslx.ts +2 -2
  7. package/dist/client/App.js +3 -9
  8. package/dist/client/App.js.map +1 -1
  9. package/dist/client/components/front-matter.js +11 -25
  10. package/dist/client/components/front-matter.js.map +1 -1
  11. package/dist/client/components/gallery/components/figure-caption.js +6 -4
  12. package/dist/client/components/gallery/components/figure-caption.js.map +1 -1
  13. package/dist/client/components/gallery/components/figure-header.js +3 -3
  14. package/dist/client/components/gallery/components/figure-header.js.map +1 -1
  15. package/dist/client/components/gallery/components/lightbox.js +13 -13
  16. package/dist/client/components/gallery/components/lightbox.js.map +1 -1
  17. package/dist/client/components/gallery/components/loading-image.js +11 -10
  18. package/dist/client/components/gallery/components/loading-image.js.map +1 -1
  19. package/dist/client/components/gallery/hooks/use-gallery-images.js +31 -7
  20. package/dist/client/components/gallery/hooks/use-gallery-images.js.map +1 -1
  21. package/dist/client/components/gallery/index.js +22 -15
  22. package/dist/client/components/gallery/index.js.map +1 -1
  23. package/dist/client/components/header.js +5 -3
  24. package/dist/client/components/header.js.map +1 -1
  25. package/dist/client/components/mdx-components.js +42 -8
  26. package/dist/client/components/mdx-components.js.map +1 -1
  27. package/dist/client/components/post-list.js +97 -90
  28. package/dist/client/components/post-list.js.map +1 -1
  29. package/dist/client/components/running-bar.js +1 -1
  30. package/dist/client/components/running-bar.js.map +1 -1
  31. package/dist/client/components/slide.js +18 -0
  32. package/dist/client/components/slide.js.map +1 -0
  33. package/dist/client/components/slides-renderer.js +7 -71
  34. package/dist/client/components/slides-renderer.js.map +1 -1
  35. package/dist/client/hooks/use-mdx-content.js +55 -9
  36. package/dist/client/hooks/use-mdx-content.js.map +1 -1
  37. package/dist/client/main.js +1 -0
  38. package/dist/client/main.js.map +1 -1
  39. package/dist/client/pages/content-router.js +19 -0
  40. package/dist/client/pages/content-router.js.map +1 -0
  41. package/dist/client/pages/home.js +11 -7
  42. package/dist/client/pages/home.js.map +1 -1
  43. package/dist/client/pages/post.js +8 -20
  44. package/dist/client/pages/post.js.map +1 -1
  45. package/dist/client/pages/slides.js +62 -86
  46. package/dist/client/pages/slides.js.map +1 -1
  47. package/dist/client/plugin/src/client.js +58 -96
  48. package/dist/client/plugin/src/client.js.map +1 -1
  49. package/dist/client/plugin/src/directory-tree.js +111 -0
  50. package/dist/client/plugin/src/directory-tree.js.map +1 -0
  51. package/index.html +1 -1
  52. package/package.json +27 -15
  53. package/plugin/src/client.tsx +64 -116
  54. package/plugin/src/directory-tree.ts +171 -0
  55. package/plugin/src/lib.ts +6 -249
  56. package/plugin/src/plugin.ts +93 -50
  57. package/plugin/src/remark-slides.ts +100 -0
  58. package/plugin/src/types.ts +22 -0
  59. package/src/App.tsx +3 -6
  60. package/src/components/front-matter.tsx +14 -29
  61. package/src/components/gallery/components/figure-caption.tsx +15 -7
  62. package/src/components/gallery/components/figure-header.tsx +3 -3
  63. package/src/components/gallery/components/lightbox.tsx +15 -13
  64. package/src/components/gallery/components/loading-image.tsx +15 -12
  65. package/src/components/gallery/hooks/use-gallery-images.ts +51 -10
  66. package/src/components/gallery/index.tsx +32 -26
  67. package/src/components/header.tsx +14 -9
  68. package/src/components/mdx-components.tsx +61 -8
  69. package/src/components/post-list.tsx +149 -115
  70. package/src/components/running-bar.tsx +1 -1
  71. package/src/components/slide.tsx +22 -5
  72. package/src/components/slides-renderer.tsx +7 -115
  73. package/src/components/welcome.tsx +11 -14
  74. package/src/hooks/use-mdx-content.ts +94 -9
  75. package/src/index.css +159 -0
  76. package/src/main.tsx +1 -0
  77. package/src/pages/content-router.tsx +27 -0
  78. package/src/pages/home.tsx +16 -2
  79. package/src/pages/post.tsx +10 -13
  80. package/src/pages/slides.tsx +75 -88
  81. package/src/vite-env.d.ts +7 -17
  82. package/vite.config.ts +25 -6
@@ -1 +1 @@
1
- {"version":3,"file":"header.js","sources":["../../../src/components/header.tsx"],"sourcesContent":["import { Link } from \"react-router-dom\";\nimport { ModeToggle } from \"./mode-toggle\";\nimport { SiGithub } from \"@icons-pack/react-simple-icons\";\nimport { ChevronUp, ChevronDown } from \"lucide-react\";\n\ninterface HeaderProps {\n slideControls?: {\n current: number;\n total: number;\n onPrevious: () => void;\n onNext: () => void;\n };\n}\n\nexport function Header({ slideControls }: HeaderProps = {}) {\n return (\n <header className=\"print:hidden fixed top-0 left-0 right-0 z-40\">\n <div className=\"mx-auto w-full px-[var(--page-padding)] flex items-center gap-8 py-4\">\n <nav className=\"flex items-center gap-1\">\n <Link\n to=\"/\"\n className=\"rounded-lg font-mono py-1.5 text-sm font-medium text-muted-foreground hover:underline\"\n >\n pl\n </Link>\n </nav>\n\n <div className=\"flex-1\" />\n\n {/* Slide navigation controls */}\n {slideControls && (\n <nav className=\"flex items-center gap-1\">\n <button\n onClick={slideControls.onPrevious}\n className=\"p-1.5 text-muted-foreground/70 hover:text-foreground transition-colors duration-200\"\n title=\"Previous slide (↑)\"\n >\n <ChevronUp className=\"h-4 w-4\" />\n </button>\n <span className=\"font-mono text-xs text-muted-foreground/70 tabular-nums min-w-[3ch] text-center\">\n {slideControls.current + 1}/{slideControls.total}\n </span>\n <button\n onClick={slideControls.onNext}\n className=\"p-1.5 text-muted-foreground/70 hover:text-foreground transition-colors duration-200\"\n title=\"Next slide (↓)\"\n >\n <ChevronDown className=\"h-4 w-4\" />\n </button>\n </nav>\n )}\n\n {/* Navigation */}\n <nav className=\"flex items-center gap-2\">\n <Link\n to=\"https://github.com/eoinmurray/pinglab\"\n target=\"_blank\"\n className=\"text-muted-foreground/70 hover:text-foreground transition-colors duration-300\"\n aria-label=\"GitHub\"\n >\n <SiGithub className=\"h-4 w-4\" />\n </Link>\n <ModeToggle />\n </nav>\n </div>\n </header>\n );\n}\n"],"names":[],"mappings":";;;;;AAcO,SAAS,OAAO,EAAE,cAAA,IAA+B,IAAI;AAC1D,6BACG,UAAA,EAAO,WAAU,gDAChB,UAAA,qBAAC,OAAA,EAAI,WAAU,wEACb,UAAA;AAAA,IAAA,oBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,WAAU;AAAA,QACX,UAAA;AAAA,MAAA;AAAA,IAAA,GAGH;AAAA,IAEA,oBAAC,OAAA,EAAI,WAAU,SAAA,CAAS;AAAA,IAGvB,iBACC,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS,cAAc;AAAA,UACvB,WAAU;AAAA,UACV,OAAM;AAAA,UAEN,UAAA,oBAAC,WAAA,EAAU,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,MAEjC,qBAAC,QAAA,EAAK,WAAU,mFACb,UAAA;AAAA,QAAA,cAAc,UAAU;AAAA,QAAE;AAAA,QAAE,cAAc;AAAA,MAAA,GAC7C;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS,cAAc;AAAA,UACvB,WAAU;AAAA,UACV,OAAM;AAAA,UAEN,UAAA,oBAAC,aAAA,EAAY,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,IACnC,GACF;AAAA,IAIF,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAG;AAAA,UACH,QAAO;AAAA,UACP,WAAU;AAAA,UACV,cAAW;AAAA,UAEX,UAAA,oBAAC,UAAA,EAAS,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,0BAE/B,YAAA,CAAA,CAAW;AAAA,IAAA,EAAA,CACd;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}
1
+ {"version":3,"file":"header.js","sources":["../../../src/components/header.tsx"],"sourcesContent":["import { Link } from \"react-router-dom\";\nimport { ModeToggle } from \"./mode-toggle\";\nimport { SiGithub } from \"@icons-pack/react-simple-icons\";\nimport { ChevronUp, ChevronDown } from \"lucide-react\";\nimport siteConfig from \"virtual:veslx-config\";\n\ninterface HeaderProps {\n slideControls?: {\n current: number;\n total: number;\n onPrevious: () => void;\n onNext: () => void;\n };\n}\n\nexport function Header({ slideControls }: HeaderProps = {}) {\n const config = siteConfig;\n\n return (\n <header className=\"print:hidden fixed top-0 left-0 right-0 z-40\">\n <div className=\"mx-auto w-full px-[var(--page-padding)] flex items-center gap-8 py-4\">\n <nav className=\"flex items-center gap-1\">\n <Link\n to=\"/\"\n className=\"rounded-lg font-mono py-1.5 text-sm font-medium text-muted-foreground hover:underline\"\n >\n {config.name}\n </Link>\n </nav>\n\n <div className=\"flex-1\" />\n\n {/* Slide navigation controls */}\n {slideControls && (\n <nav className=\"flex items-center gap-1\">\n <button\n onClick={slideControls.onPrevious}\n className=\"p-1.5 text-muted-foreground/70 hover:text-foreground transition-colors duration-200\"\n title=\"Previous slide (↑)\"\n >\n <ChevronUp className=\"h-4 w-4\" />\n </button>\n <span className=\"font-mono text-xs text-muted-foreground/70 tabular-nums min-w-[3ch] text-center\">\n {slideControls.current + 1}/{slideControls.total}\n </span>\n <button\n onClick={slideControls.onNext}\n className=\"p-1.5 text-muted-foreground/70 hover:text-foreground transition-colors duration-200\"\n title=\"Next slide (↓)\"\n >\n <ChevronDown className=\"h-4 w-4\" />\n </button>\n </nav>\n )}\n\n {/* Navigation */}\n <nav className=\"flex items-center gap-2\">\n {config.github && (\n <Link\n to={`https://github.com/${config.github}`}\n target=\"_blank\"\n className=\"text-muted-foreground/70 hover:text-foreground transition-colors duration-300\"\n aria-label=\"GitHub\"\n >\n <SiGithub className=\"h-4 w-4\" />\n </Link>\n )}\n <ModeToggle />\n </nav>\n </div>\n </header>\n );\n}\n"],"names":[],"mappings":";;;;;;AAeO,SAAS,OAAO,EAAE,cAAA,IAA+B,IAAI;AAC1D,QAAM,SAAS;AAEf,6BACG,UAAA,EAAO,WAAU,gDAChB,UAAA,qBAAC,OAAA,EAAI,WAAU,wEACb,UAAA;AAAA,IAAA,oBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,WAAU;AAAA,QAET,UAAA,OAAO;AAAA,MAAA;AAAA,IAAA,GAEZ;AAAA,IAEA,oBAAC,OAAA,EAAI,WAAU,SAAA,CAAS;AAAA,IAGvB,iBACC,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS,cAAc;AAAA,UACvB,WAAU;AAAA,UACV,OAAM;AAAA,UAEN,UAAA,oBAAC,WAAA,EAAU,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,MAEjC,qBAAC,QAAA,EAAK,WAAU,mFACb,UAAA;AAAA,QAAA,cAAc,UAAU;AAAA,QAAE;AAAA,QAAE,cAAc;AAAA,MAAA,GAC7C;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS,cAAc;AAAA,UACvB,WAAU;AAAA,UACV,OAAM;AAAA,UAEN,UAAA,oBAAC,aAAA,EAAY,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,IACnC,GACF;AAAA,IAIF,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,MAAA,OAAO,UACN;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI,sBAAsB,OAAO,MAAM;AAAA,UACvC,QAAO;AAAA,UACP,WAAU;AAAA,UACV,cAAW;AAAA,UAEX,UAAA,oBAAC,UAAA,EAAS,WAAU,UAAA,CAAU;AAAA,QAAA;AAAA,MAAA;AAAA,0BAGjC,YAAA,CAAA,CAAW;AAAA,IAAA,EAAA,CACd;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}
@@ -1,11 +1,51 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { useLocation, Link } from "react-router-dom";
2
3
  import Gallery from "./gallery/index.js";
3
4
  import { ParameterTable } from "./parameter-table.js";
4
5
  import { ParameterBadge } from "./parameter-badge.js";
6
+ import { FrontMatter } from "./front-matter.js";
7
+ function SmartLink({ href, children, ...props }) {
8
+ const location = useLocation();
9
+ const isExternal = (href == null ? void 0 : href.startsWith("http")) || (href == null ? void 0 : href.startsWith("mailto:")) || (href == null ? void 0 : href.startsWith("tel:"));
10
+ const isHashOnly = href == null ? void 0 : href.startsWith("#");
11
+ if (isExternal || isHashOnly || !href) {
12
+ return /* @__PURE__ */ jsx(
13
+ "a",
14
+ {
15
+ href,
16
+ className: "text-primary hover:underline underline-offset-2",
17
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {},
18
+ ...props,
19
+ children
20
+ }
21
+ );
22
+ }
23
+ let resolvedHref = href;
24
+ if (href.startsWith("./") || href.startsWith("../")) {
25
+ const currentPath = location.pathname;
26
+ const currentDir = currentPath.replace(/\/[^/]+\.mdx$/, "") || currentPath.replace(/\/[^/]*$/, "") || "/";
27
+ if (href.startsWith("./")) {
28
+ resolvedHref = `${currentDir}/${href.slice(2)}`.replace(/\/+/g, "/");
29
+ } else if (href.startsWith("../")) {
30
+ const parentDir = currentDir.replace(/\/[^/]+$/, "") || "/";
31
+ resolvedHref = `${parentDir}/${href.slice(3)}`.replace(/\/+/g, "/");
32
+ }
33
+ }
34
+ return /* @__PURE__ */ jsx(
35
+ Link,
36
+ {
37
+ to: resolvedHref,
38
+ className: "text-primary hover:underline underline-offset-2",
39
+ ...props,
40
+ children
41
+ }
42
+ );
43
+ }
5
44
  function generateId(children) {
6
45
  return (children == null ? void 0 : children.toString().toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-")) ?? "";
7
46
  }
8
47
  const mdxComponents = {
48
+ FrontMatter,
9
49
  Gallery,
10
50
  ParameterTable,
11
51
  ParameterBadge,
@@ -99,14 +139,8 @@ const mdxComponents = {
99
139
  ul: (props) => /* @__PURE__ */ jsx("ul", { className: "my-4 ml-6 list-disc marker:text-muted-foreground", ...props }),
100
140
  ol: (props) => /* @__PURE__ */ jsx("ol", { className: "my-4 ml-6 list-decimal marker:text-muted-foreground", ...props }),
101
141
  li: (props) => /* @__PURE__ */ jsx("li", { className: "mt-1.5", ...props }),
102
- // Links
103
- a: (props) => /* @__PURE__ */ jsx(
104
- "a",
105
- {
106
- className: "text-primary hover:underline underline-offset-2",
107
- ...props
108
- }
109
- ),
142
+ // Links - uses React Router for internal navigation
143
+ a: SmartLink,
110
144
  // Tables
111
145
  table: (props) => /* @__PURE__ */ jsx("div", { className: "not-prose my-6 overflow-x-auto border border-border rounded-md", children: /* @__PURE__ */ jsx("table", { className: "w-full text-sm border-collapse", ...props }) }),
112
146
  thead: (props) => /* @__PURE__ */ jsx("thead", { className: "bg-muted/50", ...props }),
@@ -1 +1 @@
1
- {"version":3,"file":"mdx-components.js","sources":["../../../src/components/mdx-components.tsx"],"sourcesContent":["\nimport Gallery from '@/components/gallery'\nimport { ParameterTable } from '@/components/parameter-table'\nimport { ParameterBadge } from '@/components/parameter-badge'\n\nfunction generateId(children: unknown): string {\n return children\n ?.toString()\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-') ?? ''\n}\n\n// Shared MDX components - lab notebook / coder aesthetic\nexport const mdxComponents = {\n Gallery,\n\n ParameterTable,\n\n ParameterBadge,\n\n // Headings - clean sans-serif\n h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h1\n id={id}\n className=\"text-2xl font-semibold tracking-tight mt-12 mb-4 first:mt-0\"\n {...props}\n />\n )\n },\n h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h2\n id={id}\n className=\"text-xl font-semibold tracking-tight mt-10 mb-3 pb-2 border-b border-border\"\n {...props}\n />\n )\n },\n h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h3\n id={id}\n className=\"text-lg font-medium tracking-tight mt-8 mb-2\"\n {...props}\n />\n )\n },\n h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h4\n id={id}\n className=\"text-base font-medium mt-6 mb-2\"\n {...props}\n />\n )\n },\n h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h5\n id={id}\n className=\"text-sm font-medium mt-4 mb-1\"\n {...props}\n />\n )\n },\n\n // Code blocks - IDE/terminal style\n pre: (props: React.HTMLAttributes<HTMLPreElement>) => (\n <pre\n className=\"not-prose w-full overflow-x-auto p-4 text-sm bg-muted/50 border border-border rounded-md font-mono my-6\"\n {...props}\n />\n ),\n code: (props: React.HTMLAttributes<HTMLElement> & { className?: string }) => {\n const isInline = !props.className?.includes('language-')\n if (isInline) {\n return (\n <code\n className=\"font-mono text-[0.85em] bg-muted px-1.5 py-0.5 rounded text-primary\"\n {...props}\n />\n )\n }\n return <code {...props} />\n },\n\n // Blockquote\n blockquote: (props: React.HTMLAttributes<HTMLQuoteElement>) => (\n <blockquote\n className=\"border-l-2 border-primary pl-4 my-6 text-muted-foreground\"\n {...props}\n />\n ),\n\n // Lists\n ul: (props: React.HTMLAttributes<HTMLUListElement>) => (\n <ul className=\"my-4 ml-6 list-disc marker:text-muted-foreground\" {...props} />\n ),\n ol: (props: React.HTMLAttributes<HTMLOListElement>) => (\n <ol className=\"my-4 ml-6 list-decimal marker:text-muted-foreground\" {...props} />\n ),\n li: (props: React.HTMLAttributes<HTMLLIElement>) => (\n <li className=\"mt-1.5\" {...props} />\n ),\n\n // Links\n a: (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (\n <a\n className=\"text-primary hover:underline underline-offset-2\"\n {...props}\n />\n ),\n\n // Tables\n table: (props: React.TableHTMLAttributes<HTMLTableElement>) => (\n <div className=\"not-prose my-6 overflow-x-auto border border-border rounded-md\">\n <table className=\"w-full text-sm border-collapse\" {...props} />\n </div>\n ),\n thead: (props: React.HTMLAttributes<HTMLTableSectionElement>) => (\n <thead className=\"bg-muted/50\" {...props} />\n ),\n tbody: (props: React.HTMLAttributes<HTMLTableSectionElement>) => (\n <tbody {...props} />\n ),\n tr: (props: React.HTMLAttributes<HTMLTableRowElement>) => (\n <tr className=\"border-b border-border last:border-b-0\" {...props} />\n ),\n th: (props: React.ThHTMLAttributes<HTMLTableCellElement>) => (\n <th\n className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\"\n {...props}\n />\n ),\n td: (props: React.TdHTMLAttributes<HTMLTableCellElement>) => (\n <td className=\"px-4 py-3 align-top\" {...props} />\n ),\n\n // Horizontal rule\n hr: (props: React.HTMLAttributes<HTMLHRElement>) => (\n <hr className=\"my-8 border-t border-border\" {...props} />\n ),\n\n // Paragraph\n p: (props: React.HTMLAttributes<HTMLParagraphElement>) => (\n <p className=\"leading-relaxed mb-4 last:mb-0\" {...props} />\n ),\n\n // Strong/emphasis\n strong: (props: React.HTMLAttributes<HTMLElement>) => (\n <strong className=\"font-semibold\" {...props} />\n ),\n em: (props: React.HTMLAttributes<HTMLElement>) => (\n <em className=\"italic\" {...props} />\n ),\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mdx-components.js","sources":["../../../src/components/mdx-components.tsx"],"sourcesContent":["import { Link, useLocation } from 'react-router-dom'\nimport Gallery from '@/components/gallery'\nimport { ParameterTable } from '@/components/parameter-table'\nimport { ParameterBadge } from '@/components/parameter-badge'\nimport { FrontMatter } from './front-matter'\n\n/**\n * Smart link component that uses React Router for internal links\n * and regular anchor tags for external links.\n */\nfunction SmartLink({ href, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) {\n const location = useLocation()\n\n // External links: absolute URLs, mailto, tel, etc.\n const isExternal = href?.startsWith('http') || href?.startsWith('mailto:') || href?.startsWith('tel:')\n\n // Hash-only links stay as anchors for in-page navigation\n const isHashOnly = href?.startsWith('#')\n\n if (isExternal || isHashOnly || !href) {\n return (\n <a\n href={href}\n className=\"text-primary hover:underline underline-offset-2\"\n {...(isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n {...props}\n >\n {children}\n </a>\n )\n }\n\n // Resolve relative paths (./foo.mdx, ../bar.mdx) against current location\n let resolvedHref = href\n if (href.startsWith('./') || href.startsWith('../')) {\n // Get current directory from pathname\n const currentPath = location.pathname\n const currentDir = currentPath.replace(/\\/[^/]+\\.mdx$/, '') || currentPath.replace(/\\/[^/]*$/, '') || '/'\n\n // Simple relative path resolution\n if (href.startsWith('./')) {\n resolvedHref = `${currentDir}/${href.slice(2)}`.replace(/\\/+/g, '/')\n } else if (href.startsWith('../')) {\n const parentDir = currentDir.replace(/\\/[^/]+$/, '') || '/'\n resolvedHref = `${parentDir}/${href.slice(3)}`.replace(/\\/+/g, '/')\n }\n }\n\n // Internal link - use React Router Link\n return (\n <Link\n to={resolvedHref}\n className=\"text-primary hover:underline underline-offset-2\"\n {...props}\n >\n {children}\n </Link>\n )\n}\n\nfunction generateId(children: unknown): string {\n return children\n ?.toString()\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-') ?? ''\n}\n\n// Shared MDX components - lab notebook / coder aesthetic\nexport const mdxComponents = {\n\n FrontMatter,\n\n Gallery,\n\n ParameterTable,\n\n ParameterBadge,\n\n // Headings - clean sans-serif\n h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h1\n id={id}\n className=\"text-2xl font-semibold tracking-tight mt-12 mb-4 first:mt-0\"\n {...props}\n />\n )\n },\n h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h2\n id={id}\n className=\"text-xl font-semibold tracking-tight mt-10 mb-3 pb-2 border-b border-border\"\n {...props}\n />\n )\n },\n h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h3\n id={id}\n className=\"text-lg font-medium tracking-tight mt-8 mb-2\"\n {...props}\n />\n )\n },\n h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h4\n id={id}\n className=\"text-base font-medium mt-6 mb-2\"\n {...props}\n />\n )\n },\n h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => {\n const id = generateId(props.children)\n return (\n <h5\n id={id}\n className=\"text-sm font-medium mt-4 mb-1\"\n {...props}\n />\n )\n },\n\n // Code blocks - IDE/terminal style\n pre: (props: React.HTMLAttributes<HTMLPreElement>) => (\n <pre\n className=\"not-prose w-full overflow-x-auto p-4 text-sm bg-muted/50 border border-border rounded-md font-mono my-6\"\n {...props}\n />\n ),\n code: (props: React.HTMLAttributes<HTMLElement> & { className?: string }) => {\n const isInline = !props.className?.includes('language-')\n if (isInline) {\n return (\n <code\n className=\"font-mono text-[0.85em] bg-muted px-1.5 py-0.5 rounded text-primary\"\n {...props}\n />\n )\n }\n return <code {...props} />\n },\n\n // Blockquote\n blockquote: (props: React.HTMLAttributes<HTMLQuoteElement>) => (\n <blockquote\n className=\"border-l-2 border-primary pl-4 my-6 text-muted-foreground\"\n {...props}\n />\n ),\n\n // Lists\n ul: (props: React.HTMLAttributes<HTMLUListElement>) => (\n <ul className=\"my-4 ml-6 list-disc marker:text-muted-foreground\" {...props} />\n ),\n ol: (props: React.HTMLAttributes<HTMLOListElement>) => (\n <ol className=\"my-4 ml-6 list-decimal marker:text-muted-foreground\" {...props} />\n ),\n li: (props: React.HTMLAttributes<HTMLLIElement>) => (\n <li className=\"mt-1.5\" {...props} />\n ),\n\n // Links - uses React Router for internal navigation\n a: SmartLink,\n\n // Tables\n table: (props: React.TableHTMLAttributes<HTMLTableElement>) => (\n <div className=\"not-prose my-6 overflow-x-auto border border-border rounded-md\">\n <table className=\"w-full text-sm border-collapse\" {...props} />\n </div>\n ),\n thead: (props: React.HTMLAttributes<HTMLTableSectionElement>) => (\n <thead className=\"bg-muted/50\" {...props} />\n ),\n tbody: (props: React.HTMLAttributes<HTMLTableSectionElement>) => (\n <tbody {...props} />\n ),\n tr: (props: React.HTMLAttributes<HTMLTableRowElement>) => (\n <tr className=\"border-b border-border last:border-b-0\" {...props} />\n ),\n th: (props: React.ThHTMLAttributes<HTMLTableCellElement>) => (\n <th\n className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\"\n {...props}\n />\n ),\n td: (props: React.TdHTMLAttributes<HTMLTableCellElement>) => (\n <td className=\"px-4 py-3 align-top\" {...props} />\n ),\n\n // Horizontal rule\n hr: (props: React.HTMLAttributes<HTMLHRElement>) => (\n <hr className=\"my-8 border-t border-border\" {...props} />\n ),\n\n // Paragraph\n p: (props: React.HTMLAttributes<HTMLParagraphElement>) => (\n <p className=\"leading-relaxed mb-4 last:mb-0\" {...props} />\n ),\n\n // Strong/emphasis\n strong: (props: React.HTMLAttributes<HTMLElement>) => (\n <strong className=\"font-semibold\" {...props} />\n ),\n em: (props: React.HTMLAttributes<HTMLElement>) => (\n <em className=\"italic\" {...props} />\n ),\n}\n"],"names":[],"mappings":"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"}
@@ -1,116 +1,123 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Link } from "react-router-dom";
3
3
  import { cn } from "../lib/utils.js";
4
- import { findReadme, findSlides } from "../plugin/src/client.js";
4
+ import { findMdxFiles, findReadme, findSlides } from "../plugin/src/client.js";
5
5
  import { formatDate } from "../lib/format-date.js";
6
6
  import { ArrowRight } from "lucide-react";
7
+ function extractOrder(name) {
8
+ const match = name.match(/^(\d+)-/);
9
+ return match ? parseInt(match[1], 10) : null;
10
+ }
11
+ function stripNumericPrefix(name) {
12
+ return name.replace(/^\d+-/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
13
+ }
7
14
  function PostList({ directory }) {
8
15
  const folders = directory.children.filter((c) => c.type === "directory");
9
- if (folders.length === 0) {
10
- 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" }) });
11
- }
12
- let posts = folders.map((folder) => {
16
+ const standaloneFiles = findMdxFiles(directory);
17
+ const folderPosts = folders.map((folder) => {
13
18
  const readme = findReadme(folder);
14
19
  const slides = findSlides(folder);
15
20
  return {
16
- ...folder,
21
+ type: "folder",
22
+ name: folder.name,
23
+ path: folder.path,
17
24
  readme,
18
- slides
25
+ slides,
26
+ file: null
19
27
  };
20
28
  });
29
+ const filePosts = standaloneFiles.map((file) => ({
30
+ type: "file",
31
+ name: file.name.replace(/\.mdx?$/, ""),
32
+ path: file.path,
33
+ readme: null,
34
+ slides: null,
35
+ file
36
+ }));
37
+ let posts = [...folderPosts, ...filePosts];
38
+ if (posts.length === 0) {
39
+ 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" }) });
40
+ }
21
41
  posts = posts.filter((post) => {
22
42
  var _a, _b;
23
- return ((_b = (_a = post.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.visibility) !== "hidden";
43
+ const frontmatter = ((_a = post.readme) == null ? void 0 : _a.frontmatter) || ((_b = post.file) == null ? void 0 : _b.frontmatter);
44
+ return (frontmatter == null ? void 0 : frontmatter.visibility) !== "hidden" && (frontmatter == null ? void 0 : frontmatter.draft) !== true;
24
45
  });
46
+ const getFrontmatter = (post) => {
47
+ var _a, _b, _c;
48
+ 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);
49
+ };
50
+ const getPostDate = (post) => {
51
+ const frontmatter = getFrontmatter(post);
52
+ return (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
53
+ };
25
54
  posts = posts.sort((a, b) => {
26
- var _a, _b, _c, _d, _e, _f;
27
- let aDate = ((_b = (_a = a.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.date) ? new Date(a.readme.frontmatter.date) : null;
28
- let bDate = ((_d = (_c = b.readme) == null ? void 0 : _c.frontmatter) == null ? void 0 : _d.date) ? new Date(b.readme.frontmatter.date) : null;
29
- if (!aDate && a.slides) {
30
- aDate = ((_e = a.slides.frontmatter) == null ? void 0 : _e.date) ? new Date(a.slides.frontmatter.date) : null;
31
- }
32
- if (!bDate && b.slides) {
33
- bDate = ((_f = b.slides.frontmatter) == null ? void 0 : _f.date) ? new Date(b.slides.frontmatter.date) : null;
55
+ var _a, _b;
56
+ const aOrder = extractOrder(a.name);
57
+ const bOrder = extractOrder(b.name);
58
+ const aDate = getPostDate(a);
59
+ const bDate = getPostDate(b);
60
+ if (aOrder !== null && bOrder !== null) {
61
+ return aOrder - bOrder;
34
62
  }
63
+ if (aOrder !== null) return -1;
64
+ if (bOrder !== null) return 1;
35
65
  if (aDate && bDate) {
36
66
  return bDate.getTime() - aDate.getTime();
37
- } else if (aDate) {
38
- return -1;
39
- } else if (bDate) {
40
- return 1;
41
- } else {
42
- return a.name.localeCompare(b.name);
43
67
  }
68
+ if (aDate) return -1;
69
+ if (bDate) return 1;
70
+ const aTitle = ((_a = getFrontmatter(a)) == null ? void 0 : _a.title) || a.name;
71
+ const bTitle = ((_b = getFrontmatter(b)) == null ? void 0 : _b.title) || b.name;
72
+ return aTitle.localeCompare(bTitle);
44
73
  });
45
- const postsGroupedByMonthAndYear = {};
46
- posts.forEach((post) => {
47
- var _a, _b, _c;
48
- let date = ((_b = (_a = post.readme) == null ? void 0 : _a.frontmatter) == null ? void 0 : _b.date) ? new Date(post.readme.frontmatter.date) : null;
49
- if (!date && post.slides) {
50
- date = ((_c = post.slides.frontmatter) == null ? void 0 : _c.date) ? new Date(post.slides.frontmatter.date) : null;
51
- }
52
- const monthYear = date ? `${date.getFullYear()}-${date.getMonth() + 1}` : "unknown";
53
- if (!postsGroupedByMonthAndYear[monthYear]) {
54
- postsGroupedByMonthAndYear[monthYear] = [];
74
+ return /* @__PURE__ */ jsx("div", { className: "space-y-1", children: posts.map((post) => {
75
+ const frontmatter = getFrontmatter(post);
76
+ const title = (frontmatter == null ? void 0 : frontmatter.title) || stripNumericPrefix(post.name);
77
+ const description = frontmatter == null ? void 0 : frontmatter.description;
78
+ const date = (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
79
+ let linkPath;
80
+ if (post.file) {
81
+ linkPath = `/${post.file.path}`;
82
+ } else if (post.slides && !post.readme) {
83
+ linkPath = `/${post.slides.path}`;
84
+ } else if (post.readme) {
85
+ linkPath = `/${post.readme.path}`;
86
+ } else {
87
+ linkPath = `/${post.path}`;
55
88
  }
56
- postsGroupedByMonthAndYear[monthYear].push(post);
57
- });
58
- return /* @__PURE__ */ jsx("div", { className: "space-y-8", children: Object.entries(postsGroupedByMonthAndYear).sort(([a], [b]) => {
59
- if (a === "unknown") return 1;
60
- if (b === "unknown") return -1;
61
- return b.localeCompare(a);
62
- }).map(([monthYear, monthPosts]) => {
63
- const [year, month] = monthYear.split("-");
64
- const displayDate = monthYear === "unknown" ? "Unknown Date" : new Date(parseInt(year), parseInt(month) - 1).toLocaleDateString("en-US", {
65
- year: "numeric",
66
- month: "long"
67
- });
68
- return /* @__PURE__ */ jsxs("div", { children: [
69
- /* @__PURE__ */ jsx("h2", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground mb-3", children: displayDate }),
70
- /* @__PURE__ */ jsx("div", { className: "space-y-1", children: monthPosts.map((post) => {
71
- var _a;
72
- let frontmatter = (_a = post.readme) == null ? void 0 : _a.frontmatter;
73
- if (!post.readme && post.slides) {
74
- frontmatter = post.slides.frontmatter;
75
- }
76
- const title = (frontmatter == null ? void 0 : frontmatter.title) || post.name;
77
- const description = frontmatter == null ? void 0 : frontmatter.description;
78
- const date = (frontmatter == null ? void 0 : frontmatter.date) ? new Date(frontmatter.date) : null;
79
- return /* @__PURE__ */ jsx(
80
- Link,
81
- {
82
- to: post.slides && !post.readme ? `/${post.slides.path}` : `/${post.readme.path}`,
83
- className: cn(
84
- "group block py-3 px-3 -mx-3 rounded-md",
85
- "transition-colors duration-150"
86
- // "hover:bg-accent"
87
- ),
88
- children: /* @__PURE__ */ jsxs("article", { className: "flex items-start gap-4", children: [
89
- /* @__PURE__ */ jsx(
90
- "time",
91
- {
92
- dateTime: date == null ? void 0 : date.toISOString(),
93
- className: "font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5",
94
- children: date ? formatDate(date) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/30", children: "—" })
95
- }
96
- ),
97
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
98
- /* @__PURE__ */ jsxs("h3", { className: cn(
99
- "text-sm font-medium text-foreground",
100
- "group-hover:underline",
101
- "flex items-center gap-2"
102
- ), children: [
103
- /* @__PURE__ */ jsx("span", { children: title }),
104
- /* @__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" })
105
- ] }),
106
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-1 mt-0.5", children: description })
107
- ] })
108
- ] })
109
- },
110
- post.path
111
- );
112
- }) })
113
- ] }, monthYear);
89
+ return /* @__PURE__ */ jsx(
90
+ Link,
91
+ {
92
+ to: linkPath,
93
+ className: cn(
94
+ "group block py-3 px-3 -mx-3 rounded-md",
95
+ "transition-colors duration-150"
96
+ ),
97
+ children: /* @__PURE__ */ jsxs("article", { className: "flex items-start gap-4", children: [
98
+ /* @__PURE__ */ jsx(
99
+ "time",
100
+ {
101
+ dateTime: date == null ? void 0 : date.toISOString(),
102
+ className: "font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5",
103
+ children: date ? formatDate(date) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/30", children: "—" })
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
107
+ /* @__PURE__ */ jsxs("h3", { className: cn(
108
+ "text-sm font-medium text-foreground",
109
+ "group-hover:underline",
110
+ "flex items-center gap-2"
111
+ ), children: [
112
+ /* @__PURE__ */ jsx("span", { children: title }),
113
+ /* @__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" })
114
+ ] }),
115
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-1 mt-0.5", children: description })
116
+ ] })
117
+ ] })
118
+ },
119
+ post.path
120
+ );
114
121
  }) });
115
122
  }
116
123
  export {
@@ -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 { DirectoryEntry } from \"../../plugin/src/lib\";\nimport { findReadme, findSlides } from \"../../plugin/src/client\";\nimport { formatDate } from \"@/lib/format-date\";\nimport { ArrowRight } from \"lucide-react\";\n\nexport default function PostList({ directory }: { directory: DirectoryEntry }) {\n const folders = directory.children.filter((c): c is DirectoryEntry => c.type === \"directory\");\n\n if (folders.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 let posts = folders.map((folder) => {\n const readme = findReadme(folder);\n const slides = findSlides(folder);\n return {\n ...folder,\n readme,\n slides,\n }\n })\n\n posts = posts.filter((post) => {\n return post.readme?.frontmatter?.visibility !== \"hidden\";\n });\n\n posts = posts.sort((a, b) => {\n let aDate = a.readme?.frontmatter?.date ? new Date(a.readme.frontmatter.date as string) : null;\n let bDate = b.readme?.frontmatter?.date ? new Date(b.readme.frontmatter.date as string) : null;\n\n if (!aDate && a.slides) {\n aDate = a.slides.frontmatter?.date ? new Date(a.slides.frontmatter.date as string) : null;\n }\n if (!bDate && b.slides) {\n bDate = b.slides.frontmatter?.date ? new Date(b.slides.frontmatter.date as string) : null;\n }\n\n if (aDate && bDate) {\n return bDate.getTime() - aDate.getTime();\n } else if (aDate) {\n return -1;\n } else if (bDate) {\n return 1;\n } else {\n return a.name.localeCompare(b.name);\n }\n });\n\n const postsGroupedByMonthAndYear: { [key: string]: typeof posts } = {};\n posts.forEach((post) => {\n let date = post.readme?.frontmatter?.date ? new Date(post.readme.frontmatter.date as string) : null;\n if (!date && post.slides) {\n date = post.slides.frontmatter?.date ? new Date(post.slides.frontmatter.date as string) : null;\n }\n const monthYear = date ? `${date.getFullYear()}-${date.getMonth() + 1}` : \"unknown\";\n if (!postsGroupedByMonthAndYear[monthYear]) {\n postsGroupedByMonthAndYear[monthYear] = [];\n }\n postsGroupedByMonthAndYear[monthYear].push(post);\n });\n\n return (\n <div className=\"space-y-8\">\n {Object.entries(postsGroupedByMonthAndYear)\n .sort(([a], [b]) => {\n if (a === \"unknown\") return 1;\n if (b === \"unknown\") return -1;\n return b.localeCompare(a);\n })\n .map(([monthYear, monthPosts]) => {\n const [year, month] = monthYear.split(\"-\");\n const displayDate = monthYear === \"unknown\" \n ? \"Unknown Date\" \n : new Date(parseInt(year), parseInt(month) - 1).toLocaleDateString(\"en-US\", { \n year: \"numeric\", \n month: \"long\" \n });\n\n return (\n <div key={monthYear}>\n <h2 className=\"text-xs font-mono uppercase tracking-wider text-muted-foreground mb-3\">\n {displayDate}\n </h2>\n <div className=\"space-y-1\">\n {monthPosts.map((post) => {\n let frontmatter = post.readme?.frontmatter;\n\n if (!post.readme && post.slides) {\n frontmatter = post.slides.frontmatter;\n }\n\n const title = (frontmatter?.title as string) || post.name;\n const description = frontmatter?.description as string | undefined;\n const date = frontmatter?.date ? new Date(frontmatter.date as string) : null;\n\n return (\n <Link\n key={post.path}\n to={(post.slides && !post.readme) ? `/${post.slides.path}` : `/${post.readme.path}`}\n className={cn(\n \"group block py-3 px-3 -mx-3 rounded-md\",\n \"transition-colors duration-150\",\n // \"hover:bg-accent\"\n )}\n >\n <article className=\"flex items-start gap-4\">\n {/* Date - left side, fixed width */}\n <time\n dateTime={date?.toISOString()}\n className=\"font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5\"\n >\n {date ? formatDate(date) : <span className=\"text-muted-foreground/30\">—</span>}\n </time>\n\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 </article>\n </Link>\n );\n })}\n </div>\n </div>\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 { Link } from \"react-router-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { DirectoryEntry, FileEntry } from \"../../plugin/src/lib\";\nimport { findReadme, findSlides, findMdxFiles } from \"../../plugin/src/client\";\nimport { formatDate } from \"@/lib/format-date\";\nimport { ArrowRight } from \"lucide-react\";\n\ntype PostEntry = {\n type: 'folder' | 'file';\n name: string;\n path: string;\n readme: FileEntry | null;\n slides: FileEntry | null;\n file: FileEntry | null; // For standalone MDX files\n};\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\nexport default function PostList({ directory }: { directory: DirectoryEntry }) {\n const folders = directory.children.filter((c): c is DirectoryEntry => c.type === \"directory\");\n const standaloneFiles = findMdxFiles(directory);\n\n // Convert folders to post entries\n const folderPosts: PostEntry[] = folders.map((folder) => {\n const readme = findReadme(folder);\n const slides = findSlides(folder);\n return {\n type: 'folder' as const,\n name: folder.name,\n path: folder.path,\n readme,\n slides,\n file: null,\n };\n });\n\n // Convert standalone MDX files to post entries\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 let posts: PostEntry[] = [...folderPosts, ...filePosts];\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 = posts.filter((post) => {\n const frontmatter = post.readme?.frontmatter || post.file?.frontmatter;\n return frontmatter?.visibility !== \"hidden\" && frontmatter?.draft !== true;\n });\n\n // Helper to get frontmatter from post\n const getFrontmatter = (post: PostEntry) => {\n return post.readme?.frontmatter || post.file?.frontmatter || post.slides?.frontmatter;\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 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-start gap-4\">\n {/* Date - left side, fixed width */}\n <time\n dateTime={date?.toISOString()}\n className=\"font-mono text-xs text-muted-foreground tabular-nums w-20 flex-shrink-0 pt-0.5\"\n >\n {date ? formatDate(date) : <span className=\"text-muted-foreground/30\">—</span>}\n </time>\n\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 </article>\n </Link>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { isSimulationRunning } from "../plugin/src/client.js";
3
3
  function RunningBar() {
4
4
  const isRunning = isSimulationRunning();
5
5
  return /* @__PURE__ */ jsx(Fragment, { children: isRunning && // this should stay red not another color
6
- /* @__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: [
6
+ /* @__PURE__ */ jsx("div", { className: "running-bar 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: [
7
7
  /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current animate-pulse" }),
8
8
  /* @__PURE__ */ jsx("span", { className: "uppercase tracking-widest", children: "simulation running" }),
9
9
  /* @__PURE__ */ jsx("span", { className: "text-primary-foreground/60", children: "Page will auto-refresh on completion" })
@@ -1 +1 @@
1
- {"version":3,"file":"running-bar.js","sources":["../../../src/components/running-bar.tsx"],"sourcesContent":["import { isSimulationRunning } from \"../../plugin/src/client\";\n\n\nexport function RunningBar() {\n const isRunning = isSimulationRunning();\n\n return (\n <>\n {isRunning && (\n // this should stay red not another color\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 )\n}"],"names":[],"mappings":";;AAGO,SAAS,aAAa;AAC3B,QAAM,YAAY,oBAAA;AAElB,SACE,oBAAA,UAAA,EACG,UAAA;AAAA,sBAEE,OAAA,EAAI,WAAU,kIACb,UAAA,qBAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,oDAAA,CAAoD;AAAA,IACpE,oBAAC,QAAA,EAAK,WAAU,6BAA4B,UAAA,sBAAkB;AAAA,IAC9D,oBAAC,QAAA,EAAK,WAAU,8BAA6B,UAAA,uCAAA,CAAoC;AAAA,EAAA,EAAA,CACnF,GACF,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"running-bar.js","sources":["../../../src/components/running-bar.tsx"],"sourcesContent":["import { isSimulationRunning } from \"../../plugin/src/client\";\n\n\nexport function RunningBar() {\n const isRunning = isSimulationRunning();\n\n return (\n <>\n {isRunning && (\n // this should stay red not another color\n <div className=\"running-bar 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 )\n}"],"names":[],"mappings":";;AAGO,SAAS,aAAa;AAC3B,QAAM,YAAY,oBAAA;AAElB,SACE,oBAAA,UAAA,EACG,UAAA;AAAA,sBAEE,OAAA,EAAI,WAAU,8IACb,UAAA,qBAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,oDAAA,CAAoD;AAAA,IACpE,oBAAC,QAAA,EAAK,WAAU,6BAA4B,UAAA,sBAAkB;AAAA,IAC9D,oBAAC,QAAA,EAAK,WAAU,8BAA6B,UAAA,uCAAA,CAAoC;AAAA,EAAA,EAAA,CACnF,GACF,GAEJ;AAEJ;"}
@@ -0,0 +1,18 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ function Slide({ index, children }) {
3
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4
+ index > 0 && /* @__PURE__ */ jsx("hr", { className: "border-t border-border w-full" }),
5
+ /* @__PURE__ */ jsx(
6
+ "div",
7
+ {
8
+ className: "slide-section min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4",
9
+ "data-slide-index": index,
10
+ children: /* @__PURE__ */ jsx("div", { className: "slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-[var(--content-width)] w-full", children })
11
+ }
12
+ )
13
+ ] });
14
+ }
15
+ export {
16
+ Slide
17
+ };
18
+ //# sourceMappingURL=slide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slide.js","sources":["../../../src/components/slide.tsx"],"sourcesContent":["import { ReactNode } from 'react'\n\ninterface SlideProps {\n index: number\n children: ReactNode\n}\n\n/**\n * Slide component - wraps slide content for stacked scrollable display.\n * Each slide takes full viewport height.\n */\nexport function Slide({ index, children }: SlideProps) {\n return (\n <>\n {index > 0 && (\n <hr className=\"border-t border-border w-full\" />\n )}\n <div\n className=\"slide-section min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4\"\n data-slide-index={index}\n >\n <div className=\"slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-[var(--content-width)] w-full\">\n {children}\n </div>\n </div>\n </>\n )\n}"],"names":[],"mappings":";AAWO,SAAS,MAAM,EAAE,OAAO,YAAwB;AACrD,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA,QAAQ,KACP,oBAAC,MAAA,EAAG,WAAU,iCAAgC;AAAA,IAEhD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,oBAAkB;AAAA,QAElB,UAAA,oBAAC,OAAA,EAAI,WAAU,mIACZ,SAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,GACF;AAEJ;"}
@@ -1,75 +1,11 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { useMemo, isValidElement, Children } from "react";
1
+ import "react/jsx-runtime";
3
2
  import { mdxComponents } from "./mdx-components.js";
4
- function useSlidesFromMDX({ Content, frontmatter }) {
5
- const slides = useMemo(() => {
6
- if (!Content) {
7
- return [];
8
- }
9
- const SlideBreak = () => /* @__PURE__ */ jsx("hr", { "data-slide-break": "true" });
10
- const componentsWithBreak = {
11
- ...mdxComponents,
12
- hr: SlideBreak
13
- };
14
- const rendered = /* @__PURE__ */ jsx(Content, { components: componentsWithBreak });
15
- return splitByHr(rendered);
16
- }, [Content]);
17
- return { slides, frontmatter };
18
- }
19
- function splitByHr(element) {
20
- var _a;
21
- const slides = [[]];
22
- function traverse(node) {
23
- var _a2;
24
- if (!node) return;
25
- if (Array.isArray(node)) {
26
- node.forEach(traverse);
27
- return;
28
- }
29
- if (isValidElement(node)) {
30
- if (node.type === "hr" || node.props && node.props["data-slide-break"] === "true") {
31
- slides.push([]);
32
- return;
33
- }
34
- const nodeType = node.type;
35
- if (typeof nodeType === "function" && nodeType.name === "SlideBreak") {
36
- slides.push([]);
37
- return;
38
- }
39
- if ((_a2 = node.props) == null ? void 0 : _a2.children) {
40
- const children = Children.toArray(node.props.children);
41
- const hasHrChild = children.some(
42
- (child) => isValidElement(child) && (child.type === "hr" || child.props && child.props["data-slide-break"] === "true")
43
- );
44
- if (hasHrChild) {
45
- children.forEach((child) => {
46
- if (isValidElement(child) && (child.type === "hr" || child.props && child.props["data-slide-break"] === "true")) {
47
- slides.push([]);
48
- } else {
49
- slides[slides.length - 1].push(child);
50
- }
51
- });
52
- } else {
53
- slides[slides.length - 1].push(node);
54
- }
55
- return;
56
- }
57
- }
58
- slides[slides.length - 1].push(node);
59
- }
60
- if (isValidElement(element) && ((_a = element.props) == null ? void 0 : _a.children)) {
61
- const children = Children.toArray(element.props.children);
62
- children.forEach(traverse);
63
- } else {
64
- traverse(element);
65
- }
66
- return slides.filter((slide) => slide.length > 0);
67
- }
68
- function SlideContent({ children }) {
69
- return /* @__PURE__ */ jsx("div", { className: "slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-xl", children });
70
- }
3
+ import { Slide } from "./slide.js";
4
+ const slidesMdxComponents = {
5
+ ...mdxComponents,
6
+ Slide
7
+ };
71
8
  export {
72
- SlideContent,
73
- useSlidesFromMDX
9
+ slidesMdxComponents
74
10
  };
75
11
  //# sourceMappingURL=slides-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slides-renderer.js","sources":["../../../src/components/slides-renderer.tsx"],"sourcesContent":["import React, { Children, isValidElement, ReactNode, useMemo } from 'react'\nimport { mdxComponents } from '@/components/mdx-components'\n\ninterface SlidesRendererProps {\n Content: React.ComponentType<{ components?: Record<string, React.ComponentType> }> | null\n frontmatter?: {\n title?: string\n description?: string\n date?: string\n }\n}\n\n/**\n * Splits MDX content into slides by <hr> elements.\n * The MDX is compiled at build time, so imports work.\n * Slide boundaries are marked with --- in MDX (which becomes <hr>).\n */\nexport function useSlidesFromMDX({ Content, frontmatter }: SlidesRendererProps) {\n const slides = useMemo(() => {\n // Handle null Content (loading state)\n if (!Content) {\n return []\n }\n\n // Create a custom hr component that we can detect\n const SlideBreak = () => <hr data-slide-break=\"true\" />\n\n // Render the MDX with our custom hr\n const componentsWithBreak = {\n ...mdxComponents,\n hr: SlideBreak,\n }\n\n // Render to get the element tree\n const rendered = <Content components={componentsWithBreak} />\n\n // Split children by hr elements\n return splitByHr(rendered)\n }, [Content])\n\n return { slides, frontmatter }\n}\n\n/**\n * Recursively traverses React element tree and splits by <hr> elements\n */\nfunction splitByHr(element: ReactNode): ReactNode[][] {\n const slides: ReactNode[][] = [[]]\n\n function traverse(node: ReactNode) {\n if (!node) return\n\n if (Array.isArray(node)) {\n node.forEach(traverse)\n return\n }\n\n if (isValidElement(node)) {\n // Check if this is our slide break marker\n if (node.type === 'hr' || (node.props && node.props['data-slide-break'] === 'true')) {\n // Start a new slide\n slides.push([])\n return\n }\n\n // Check if it's an hr component from mdxComponents\n const nodeType = node.type as any\n if (typeof nodeType === 'function' && nodeType.name === 'SlideBreak') {\n slides.push([])\n return\n }\n\n // For fragments or elements with children, we need to check children\n if (node.props?.children) {\n const children = Children.toArray(node.props.children)\n\n // Check if any child is an hr - if so, we need to split this element\n const hasHrChild = children.some(child =>\n isValidElement(child) && (\n child.type === 'hr' ||\n (child.props && child.props['data-slide-break'] === 'true')\n )\n )\n\n if (hasHrChild) {\n // Split the children\n children.forEach(child => {\n if (isValidElement(child) && (\n child.type === 'hr' ||\n (child.props && child.props['data-slide-break'] === 'true')\n )) {\n slides.push([])\n } else {\n slides[slides.length - 1].push(child)\n }\n })\n } else {\n // No hr children, add the whole element\n slides[slides.length - 1].push(node)\n }\n return\n }\n }\n\n // Add to current slide\n slides[slides.length - 1].push(node)\n }\n\n // Get the children of the rendered element\n if (isValidElement(element) && element.props?.children) {\n const children = Children.toArray(element.props.children)\n children.forEach(traverse)\n } else {\n traverse(element)\n }\n\n // Filter out empty slides\n return slides.filter(slide => slide.length > 0)\n}\n\n/**\n * Renders a single slide's content\n */\nexport function SlideContent({ children }: { children: ReactNode[] }) {\n return (\n <div className=\"slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-xl\">\n {children}\n </div>\n )\n}\n"],"names":["_a"],"mappings":";;;AAiBO,SAAS,iBAAiB,EAAE,SAAS,eAAoC;AAC9E,QAAM,SAAS,QAAQ,MAAM;AAE3B,QAAI,CAAC,SAAS;AACZ,aAAO,CAAA;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,oBAAC,MAAA,EAAG,oBAAiB,QAAO;AAGrD,UAAM,sBAAsB;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI;AAAA,IAAA;AAIN,UAAM,WAAW,oBAAC,SAAA,EAAQ,YAAY,oBAAA,CAAqB;AAG3D,WAAO,UAAU,QAAQ;AAAA,EAC3B,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,QAAQ,YAAA;AACnB;AAKA,SAAS,UAAU,SAAmC;;AACpD,QAAM,SAAwB,CAAC,EAAE;AAEjC,WAAS,SAAS,MAAiB;;AACjC,QAAI,CAAC,KAAM;AAEX,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAK,QAAQ,QAAQ;AACrB;AAAA,IACF;AAEA,QAAI,eAAe,IAAI,GAAG;AAExB,UAAI,KAAK,SAAS,QAAS,KAAK,SAAS,KAAK,MAAM,kBAAkB,MAAM,QAAS;AAEnF,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AAGA,YAAM,WAAW,KAAK;AACtB,UAAI,OAAO,aAAa,cAAc,SAAS,SAAS,cAAc;AACpE,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AAGA,WAAIA,MAAA,KAAK,UAAL,gBAAAA,IAAY,UAAU;AACxB,cAAM,WAAW,SAAS,QAAQ,KAAK,MAAM,QAAQ;AAGrD,cAAM,aAAa,SAAS;AAAA,UAAK,CAAA,UAC/B,eAAe,KAAK,MAClB,MAAM,SAAS,QACd,MAAM,SAAS,MAAM,MAAM,kBAAkB,MAAM;AAAA,QAAA;AAIxD,YAAI,YAAY;AAEd,mBAAS,QAAQ,CAAA,UAAS;AACxB,gBAAI,eAAe,KAAK,MACtB,MAAM,SAAS,QACd,MAAM,SAAS,MAAM,MAAM,kBAAkB,MAAM,SACnD;AACD,qBAAO,KAAK,EAAE;AAAA,YAChB,OAAO;AACL,qBAAO,OAAO,SAAS,CAAC,EAAE,KAAK,KAAK;AAAA,YACtC;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AAEL,iBAAO,OAAO,SAAS,CAAC,EAAE,KAAK,IAAI;AAAA,QACrC;AACA;AAAA,MACF;AAAA,IACF;AAGA,WAAO,OAAO,SAAS,CAAC,EAAE,KAAK,IAAI;AAAA,EACrC;AAGA,MAAI,eAAe,OAAO,OAAK,aAAQ,UAAR,mBAAe,WAAU;AACtD,UAAM,WAAW,SAAS,QAAQ,QAAQ,MAAM,QAAQ;AACxD,aAAS,QAAQ,QAAQ;AAAA,EAC3B,OAAO;AACL,aAAS,OAAO;AAAA,EAClB;AAGA,SAAO,OAAO,OAAO,CAAA,UAAS,MAAM,SAAS,CAAC;AAChD;AAKO,SAAS,aAAa,EAAE,YAAuC;AACpE,SACE,oBAAC,OAAA,EAAI,WAAU,wGACZ,SAAA,CACH;AAEJ;"}
1
+ {"version":3,"file":"slides-renderer.js","sources":["../../../src/components/slides-renderer.tsx"],"sourcesContent":["import { ReactNode } from 'react'\nimport { mdxComponents } from '@/components/mdx-components'\nimport { Slide } from '@/components/slide'\n\n/**\n * MDX components for slides - includes the Slide component\n */\nexport const slidesMdxComponents = {\n ...mdxComponents,\n Slide,\n}\n\n/**\n * Renders a single slide's content\n */\nexport function SlideContent({ children }: { children: ReactNode }) {\n return (\n <div className=\"slide-content prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed max-w-xl\">\n {children}\n </div>\n )\n}\n"],"names":[],"mappings":";;;AAOO,MAAM,sBAAsB;AAAA,EACjC,GAAG;AAAA,EACH;AACF;"}