veslx 0.0.1

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 (83) hide show
  1. package/README.md +3 -0
  2. package/bin/lib/import-config.ts +13 -0
  3. package/bin/lib/init.ts +31 -0
  4. package/bin/lib/serve.ts +35 -0
  5. package/bin/lib/start.ts +40 -0
  6. package/bin/lib/stop.ts +24 -0
  7. package/bin/vesl.ts +41 -0
  8. package/components.json +20 -0
  9. package/eslint.config.js +23 -0
  10. package/index.html +17 -0
  11. package/package.json +89 -0
  12. package/plugin/README.md +21 -0
  13. package/plugin/package.json +26 -0
  14. package/plugin/src/cli.ts +30 -0
  15. package/plugin/src/client.tsx +224 -0
  16. package/plugin/src/lib.ts +268 -0
  17. package/plugin/src/plugin.ts +109 -0
  18. package/postcss.config.js +5 -0
  19. package/public/logo_dark.png +0 -0
  20. package/public/logo_light.png +0 -0
  21. package/src/App.tsx +21 -0
  22. package/src/components/front-matter.tsx +53 -0
  23. package/src/components/gallery/components/figure-caption.tsx +15 -0
  24. package/src/components/gallery/components/figure-header.tsx +20 -0
  25. package/src/components/gallery/components/lightbox.tsx +106 -0
  26. package/src/components/gallery/components/loading-image.tsx +48 -0
  27. package/src/components/gallery/hooks/use-gallery-images.ts +103 -0
  28. package/src/components/gallery/hooks/use-lightbox.ts +40 -0
  29. package/src/components/gallery/index.tsx +134 -0
  30. package/src/components/gallery/lib/render-math-in-text.tsx +47 -0
  31. package/src/components/header.tsx +68 -0
  32. package/src/components/index.ts +5 -0
  33. package/src/components/loading.tsx +16 -0
  34. package/src/components/mdx-components.tsx +163 -0
  35. package/src/components/mode-toggle.tsx +44 -0
  36. package/src/components/page-error.tsx +59 -0
  37. package/src/components/parameter-badge.tsx +78 -0
  38. package/src/components/parameter-table.tsx +420 -0
  39. package/src/components/post-list.tsx +148 -0
  40. package/src/components/running-bar.tsx +21 -0
  41. package/src/components/runtime-mdx.tsx +82 -0
  42. package/src/components/slide.tsx +11 -0
  43. package/src/components/theme-provider.tsx +6 -0
  44. package/src/components/ui/badge.tsx +36 -0
  45. package/src/components/ui/breadcrumb.tsx +115 -0
  46. package/src/components/ui/button.tsx +56 -0
  47. package/src/components/ui/card.tsx +79 -0
  48. package/src/components/ui/carousel.tsx +260 -0
  49. package/src/components/ui/dropdown-menu.tsx +198 -0
  50. package/src/components/ui/input.tsx +22 -0
  51. package/src/components/ui/kbd.tsx +22 -0
  52. package/src/components/ui/select.tsx +158 -0
  53. package/src/components/ui/separator.tsx +29 -0
  54. package/src/components/ui/shadcn-io/code-block/index.tsx +620 -0
  55. package/src/components/ui/shadcn-io/code-block/server.tsx +63 -0
  56. package/src/components/ui/sheet.tsx +140 -0
  57. package/src/components/ui/sidebar.tsx +771 -0
  58. package/src/components/ui/skeleton.tsx +15 -0
  59. package/src/components/ui/spinner.tsx +16 -0
  60. package/src/components/ui/tooltip.tsx +28 -0
  61. package/src/components/welcome.tsx +21 -0
  62. package/src/hooks/use-key-bindings.ts +72 -0
  63. package/src/hooks/use-mobile.tsx +19 -0
  64. package/src/index.css +279 -0
  65. package/src/lib/constants.ts +10 -0
  66. package/src/lib/format-date.tsx +6 -0
  67. package/src/lib/format-file-size.ts +10 -0
  68. package/src/lib/parameter-utils.ts +134 -0
  69. package/src/lib/utils.ts +6 -0
  70. package/src/main.tsx +10 -0
  71. package/src/pages/home.tsx +39 -0
  72. package/src/pages/post.tsx +65 -0
  73. package/src/pages/slides.tsx +173 -0
  74. package/tailwind.config.js +136 -0
  75. package/test-content/.vesl.json +49 -0
  76. package/test-content/README.md +33 -0
  77. package/test-content/test-post/README.mdx +7 -0
  78. package/test-content/test-slides/SLIDES.mdx +8 -0
  79. package/tsconfig.app.json +32 -0
  80. package/tsconfig.json +15 -0
  81. package/tsconfig.node.json +25 -0
  82. package/vesl.config.ts +4 -0
  83. package/vite.config.ts +54 -0
@@ -0,0 +1,65 @@
1
+
2
+ import { useParams } from "react-router-dom";
3
+ import { findSlides, isSimulationRunning, useDirectory, useFileContent } from "../../plugin/src/client";
4
+ import Loading from "@/components/loading";
5
+ import { FileEntry } from "plugin/src/lib";
6
+ import { FrontMatter } from "@/components/front-matter";
7
+ import { RunningBar } from "@/components/running-bar";
8
+ import { Header } from "@/components/header";
9
+ import { RuntimeMDX } from "@/components/runtime-mdx";
10
+
11
+
12
+ export function Post() {
13
+ const { "path": path = "." } = useParams();
14
+ const filePath = `${path}/README.mdx`
15
+ const { directory, file, loading, error } = useDirectory(filePath)
16
+ const { content } = useFileContent(filePath);
17
+ const isRunning = isSimulationRunning();
18
+
19
+ let slides: FileEntry | null = null;
20
+ if (directory) {
21
+ slides = findSlides(directory);
22
+ }
23
+
24
+ if (loading) return <Loading />
25
+
26
+ if (error) {
27
+ return (
28
+ <main className="min-h-screen bg-background container mx-auto max-w-4xl py-12">
29
+ <p className="text-center text-red-600">{error.message}</p>
30
+ </main>
31
+ )
32
+ }
33
+
34
+ return (
35
+ <div className="flex min-h-screen flex-col bg-background noise-overlay">
36
+ <title>{file?.frontmatter?.title}</title>
37
+ <RunningBar />
38
+ <Header />
39
+ <main className="flex-1 mx-auto w-full max-w-[var(--content-width)] px-[var(--page-padding)]">
40
+
41
+ {isRunning && (
42
+ <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">
43
+ <span className="inline-flex items-center gap-3">
44
+ <span className="h-1.5 w-1.5 rounded-full bg-current animate-pulse" />
45
+ <span className="uppercase tracking-widest">simulation running</span>
46
+ <span className="text-primary-foreground/60">Page will auto-refresh on completion</span>
47
+ </span>
48
+ </div>
49
+ )}
50
+
51
+ {file && (
52
+ <article className="my-24 prose dark:prose-invert prose-headings:tracking-tight prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline max-w-[var(--prose-width)] animate-fade-in">
53
+ <FrontMatter
54
+ title={file.frontmatter?.title}
55
+ date={file.frontmatter?.date}
56
+ description={file.frontmatter?.description}
57
+ slides={slides}
58
+ />
59
+ {content && <RuntimeMDX content={content} />}
60
+ </article>
61
+ )}
62
+ </main>
63
+ </div>
64
+ )
65
+ }
@@ -0,0 +1,173 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { FULLSCREEN_DATA_ATTR } from "@/lib/constants";
3
+ import { useDirectory, useFileContent } from "../../plugin/src/client";
4
+ import { useParams, useSearchParams } from "react-router-dom"
5
+ import Loading from "@/components/loading";
6
+ import { FrontMatter } from "@/components/front-matter";
7
+ import { RuntimeMDX } from "@/components/runtime-mdx";
8
+ import { RunningBar } from "@/components/running-bar";
9
+ import { Header } from "@/components/header";
10
+
11
+ export function parseSlideContent(content: string): string[] {
12
+ // Strip frontmatter if present (starts with --- and ends with ---)
13
+ let processedContent = content;
14
+ const frontmatterMatch = content.match(/^---\n[\s\S]*?\n---\n/);
15
+ if (frontmatterMatch) {
16
+ processedContent = content.slice(frontmatterMatch[0].length);
17
+ }
18
+
19
+ return processedContent
20
+ .split(/^---$/m)
21
+ .map((slide) => slide.trim())
22
+ .filter((slide) => slide.length > 0);
23
+ }
24
+
25
+
26
+ export function SlidesPage() {
27
+ const { "path": path = "." } = useParams();
28
+ const [searchParams, setSearchParams] = useSearchParams();
29
+ const filePath = `${path}/SLIDES.mdx`
30
+ const { file } = useDirectory(filePath)
31
+ const { content, loading, error } = useFileContent(filePath);
32
+ const slides = content ? parseSlideContent(content) : [];
33
+
34
+ // Total slides = 1 (title) + content slides
35
+ const totalSlides = slides.length + 1;
36
+
37
+ // Get initial slide from query param
38
+ const initialSlide = Math.max(0, Math.min(
39
+ parseInt(searchParams.get("slide") || "0", 10),
40
+ totalSlides - 1
41
+ ));
42
+
43
+ const [currentSlide, setCurrentSlide] = useState(initialSlide);
44
+ const slideRefs = useRef<(HTMLDivElement | null)[]>([]);
45
+
46
+ // Scroll to slide and update query param
47
+ const goToSlide = useCallback((index: number) => {
48
+ const clampedIndex = Math.max(0, Math.min(index, totalSlides - 1));
49
+ const target = slideRefs.current[clampedIndex];
50
+ if (target) {
51
+ const targetTop = target.getBoundingClientRect().top + window.scrollY;
52
+ window.scrollTo({ top: targetTop, behavior: "smooth" });
53
+ }
54
+ }, [totalSlides]);
55
+
56
+ const goToPrevious = useCallback(() => {
57
+ goToSlide(currentSlide - 1);
58
+ }, [currentSlide, goToSlide]);
59
+
60
+ const goToNext = useCallback(() => {
61
+ goToSlide(currentSlide + 1);
62
+ }, [currentSlide, goToSlide]);
63
+
64
+ // Keyboard navigation
65
+ useEffect(() => {
66
+ const handleKeyDown = (e: KeyboardEvent) => {
67
+ if (e.key === "ArrowUp" || e.key === "k") {
68
+ e.preventDefault();
69
+ goToPrevious();
70
+ } else if (e.key === "ArrowDown" || e.key === "j") {
71
+ e.preventDefault();
72
+ goToNext();
73
+ }
74
+ };
75
+
76
+ window.addEventListener("keydown", handleKeyDown);
77
+ return () => window.removeEventListener("keydown", handleKeyDown);
78
+ }, [goToPrevious, goToNext]);
79
+
80
+ // Update query param on scroll (delayed to avoid interference on load)
81
+ useEffect(() => {
82
+ let observer: IntersectionObserver | null = null;
83
+
84
+ const timeoutId = setTimeout(() => {
85
+ observer = new IntersectionObserver(
86
+ (entries) => {
87
+ for (const entry of entries) {
88
+ if (entry.isIntersecting) {
89
+ const index = slideRefs.current.findIndex((ref) => ref === entry.target);
90
+ if (index !== -1) {
91
+ setCurrentSlide(index);
92
+ setSearchParams(index > 0 ? { slide: String(index) } : {}, { replace: true });
93
+ }
94
+ }
95
+ }
96
+ },
97
+ { threshold: 0.5 }
98
+ );
99
+
100
+ slideRefs.current.forEach((ref) => {
101
+ if (ref) observer!.observe(ref);
102
+ });
103
+ }, 100);
104
+
105
+ return () => {
106
+ clearTimeout(timeoutId);
107
+ observer?.disconnect();
108
+ };
109
+ }, [slides.length, setSearchParams]);
110
+
111
+ if (loading) {
112
+ return (
113
+ <Loading />
114
+ )
115
+ }
116
+
117
+ if (error) {
118
+ return (
119
+ <main className="min-h-screen bg-background container mx-auto max-w-4xl py-12">
120
+ <p className="text-center text-red-600">{error}</p>
121
+ </main>
122
+ )
123
+ }
124
+
125
+ if (slides.length === 0) {
126
+ return (
127
+ <div className="flex items-center justify-center p-12 text-muted-foreground font-mono text-sm">
128
+ no slides found — use "---" to separate slides
129
+ </div>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <main className="slides-container">
135
+ <title>{file?.frontmatter?.title}</title>
136
+ <RunningBar />
137
+ <Header
138
+ slideControls={{
139
+ current: currentSlide,
140
+ total: totalSlides,
141
+ onPrevious: goToPrevious,
142
+ onNext: goToNext,
143
+ }}
144
+ />
145
+ <div {...{[FULLSCREEN_DATA_ATTR]: "true"}}>
146
+ <div
147
+ ref={(el) => { slideRefs.current[0] = el; }}
148
+ className="slide-page max-w-xl min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto"
149
+ >
150
+ {file && (
151
+ <FrontMatter
152
+ title={file.frontmatter?.title}
153
+ date={file.frontmatter?.date}
154
+ description={file.frontmatter?.description}
155
+ />
156
+ )}
157
+ </div>
158
+ <hr className="print:hidden" />
159
+ {slides.map((content, index) => (
160
+ <div key={index}>
161
+ <div
162
+ ref={(el) => { slideRefs.current[index + 1] = el; }}
163
+ className="slide-page min-h-[50vh] sm:min-h-[70vh] md:min-h-screen flex items-center justify-center py-8 sm:py-12 md:py-16 px-4 mx-auto"
164
+ >
165
+ {content && <RuntimeMDX content={content} size="md" />}
166
+ </div>
167
+ {index < slides.length - 1 && <hr className="print:hidden" />}
168
+ </div>
169
+ ))}
170
+ </div>
171
+ </main>
172
+ )
173
+ }
@@ -0,0 +1,136 @@
1
+ import path from "path";
2
+ import { fileURLToPath } from "url";
3
+
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+
6
+ /** @type {import('tailwindcss').Config} */
7
+ export default {
8
+ darkMode: ["class"],
9
+ content: [
10
+ "./index.html",
11
+ "./src/**/*.{js,ts,jsx,tsx}",
12
+ path.join(__dirname, "../biblio/**/*.mdx"),
13
+ path.join(__dirname, "../biblio/**/*.md"),
14
+ ],
15
+ theme: {
16
+ extend: {
17
+ fontFamily: {
18
+ // Clean sans-serif for everything
19
+ sans: ['Inter', 'system-ui', 'sans-serif'],
20
+ // Monospace for code, data, technical elements
21
+ mono: ['JetBrains Mono', 'ui-monospace', 'monospace'],
22
+ },
23
+ fontSize: {
24
+ // Refined type scale
25
+ 'xs': ['0.75rem', { lineHeight: '1.5' }],
26
+ 'sm': ['0.875rem', { lineHeight: '1.6' }],
27
+ 'base': ['1rem', { lineHeight: '1.75' }],
28
+ 'lg': ['1.125rem', { lineHeight: '1.7' }],
29
+ 'xl': ['1.25rem', { lineHeight: '1.5' }],
30
+ '2xl': ['1.5rem', { lineHeight: '1.35' }],
31
+ '3xl': ['1.875rem', { lineHeight: '1.25' }],
32
+ '4xl': ['2.25rem', { lineHeight: '1.2' }],
33
+ '5xl': ['3rem', { lineHeight: '1.15' }],
34
+ '6xl': ['3.75rem', { lineHeight: '1.1' }],
35
+ },
36
+ letterSpacing: {
37
+ 'tightest': '-0.04em',
38
+ 'tighter': '-0.02em',
39
+ 'tight': '-0.01em',
40
+ 'normal': '0',
41
+ 'wide': '0.01em',
42
+ 'wider': '0.02em',
43
+ 'widest': '0.05em',
44
+ },
45
+ borderRadius: {
46
+ lg: 'var(--radius)',
47
+ md: 'calc(var(--radius) - 2px)',
48
+ sm: 'calc(var(--radius) - 4px)'
49
+ },
50
+ colors: {
51
+ // Ministry of Unsanctioned Computation
52
+ ministry: {
53
+ parchment: '#F5F0E6',
54
+ cream: '#FAF7F0',
55
+ crimson: '#8B1538',
56
+ gold: '#B8860B',
57
+ brown: '#5C4033',
58
+ ink: '#2C1810',
59
+ },
60
+ background: 'hsl(var(--background))',
61
+ foreground: 'hsl(var(--foreground))',
62
+ card: {
63
+ DEFAULT: 'hsl(var(--card))',
64
+ foreground: 'hsl(var(--card-foreground))'
65
+ },
66
+ popover: {
67
+ DEFAULT: 'hsl(var(--popover))',
68
+ foreground: 'hsl(var(--popover-foreground))'
69
+ },
70
+ primary: {
71
+ DEFAULT: 'hsl(var(--primary))',
72
+ foreground: 'hsl(var(--primary-foreground))'
73
+ },
74
+ secondary: {
75
+ DEFAULT: 'hsl(var(--secondary))',
76
+ foreground: 'hsl(var(--secondary-foreground))'
77
+ },
78
+ muted: {
79
+ DEFAULT: 'hsl(var(--muted))',
80
+ foreground: 'hsl(var(--muted-foreground))'
81
+ },
82
+ accent: {
83
+ DEFAULT: 'hsl(var(--accent))',
84
+ foreground: 'hsl(var(--accent-foreground))'
85
+ },
86
+ destructive: {
87
+ DEFAULT: 'hsl(var(--destructive))',
88
+ foreground: 'hsl(var(--destructive-foreground))'
89
+ },
90
+ border: 'hsl(var(--border))',
91
+ input: 'hsl(var(--input))',
92
+ ring: 'hsl(var(--ring))',
93
+ chart: {
94
+ '1': 'hsl(var(--chart-1))',
95
+ '2': 'hsl(var(--chart-2))',
96
+ '3': 'hsl(var(--chart-3))',
97
+ '4': 'hsl(var(--chart-4))',
98
+ '5': 'hsl(var(--chart-5))'
99
+ },
100
+ sidebar: {
101
+ DEFAULT: 'hsl(var(--sidebar-background))',
102
+ foreground: 'hsl(var(--sidebar-foreground))',
103
+ primary: 'hsl(var(--sidebar-primary))',
104
+ 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
105
+ accent: 'hsl(var(--sidebar-accent))',
106
+ 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
107
+ border: 'hsl(var(--sidebar-border))',
108
+ ring: 'hsl(var(--sidebar-ring))'
109
+ }
110
+ },
111
+ animation: {
112
+ 'fade-in': 'fade-in 0.4s ease-out forwards',
113
+ 'fade-in-slow': 'fade-in-slow 0.6s ease-out forwards',
114
+ 'slide-up': 'slide-up 0.5s ease-out forwards',
115
+ },
116
+ keyframes: {
117
+ 'fade-in': {
118
+ from: { opacity: '0', transform: 'translateY(8px)' },
119
+ to: { opacity: '1', transform: 'translateY(0)' },
120
+ },
121
+ 'fade-in-slow': {
122
+ from: { opacity: '0' },
123
+ to: { opacity: '1' },
124
+ },
125
+ 'slide-up': {
126
+ from: { opacity: '0', transform: 'translateY(20px)' },
127
+ to: { opacity: '1', transform: 'translateY(0)' },
128
+ },
129
+ },
130
+ transitionTimingFunction: {
131
+ 'out-expo': 'cubic-bezier(0.19, 1, 0.22, 1)',
132
+ },
133
+ }
134
+ },
135
+ plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
136
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "type": "directory",
3
+ "name": "test-content",
4
+ "path": ".",
5
+ "children": [
6
+ {
7
+ "type": "file",
8
+ "name": "README.md",
9
+ "path": "README.md",
10
+ "size": 472
11
+ },
12
+ {
13
+ "type": "directory",
14
+ "name": "test-slides",
15
+ "path": "test-slides",
16
+ "children": [
17
+ {
18
+ "type": "file",
19
+ "name": "SLIDES.mdx",
20
+ "path": "test-slides/SLIDES.mdx",
21
+ "size": 122,
22
+ "frontmatter": {
23
+ "title": "Test Slides",
24
+ "description": "These are test slides for testing purposes.",
25
+ "date": "2024-06-15"
26
+ }
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "type": "directory",
32
+ "name": "test-post",
33
+ "path": "test-post",
34
+ "children": [
35
+ {
36
+ "type": "file",
37
+ "name": "README.mdx",
38
+ "path": "test-post/README.mdx",
39
+ "size": 115,
40
+ "frontmatter": {
41
+ "title": "Test Post",
42
+ "description": "This is a test post for testing purposes.",
43
+ "date": "2024-06-15"
44
+ }
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
@@ -0,0 +1,33 @@
1
+
2
+ ## Getting Started
3
+
4
+ ### Installation
5
+
6
+ First install [Task](https://taskfile.dev/docs/installation), then:
7
+
8
+ ```bash
9
+ git clone https://github.com/eoinmurray/pinglab
10
+ task install
11
+ ```
12
+
13
+ ### Simulate
14
+
15
+ Then you can run experiments like:
16
+
17
+ ```bash
18
+ task execute -- content/dynamical-regimes/experiment.py
19
+ # or
20
+ task execute:all
21
+ ```
22
+
23
+ which will generate data in the appropriate folder.
24
+
25
+ ### Present
26
+
27
+ Start the UI server with
28
+
29
+ ```bash
30
+ task dev
31
+ # or
32
+ task bg # starts in background
33
+ ```
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: "Test Post"
3
+ description: "This is a test post for testing purposes."
4
+ date: "2024-06-15"
5
+ ---
6
+
7
+ # Test Post
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: "Test Slides"
3
+ description: "These are test slides for testing purposes."
4
+ date: "2024-06-15"
5
+ ---
6
+
7
+
8
+ # Test Slides
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client", "node"],
9
+ "skipLibCheck": true,
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ "baseUrl": ".",
19
+ "paths": {
20
+ "@/*": ["./src/*"],
21
+ },
22
+
23
+ /* Linting */
24
+ "strict": true,
25
+ "noUnusedLocals": true,
26
+ "noUnusedParameters": true,
27
+ "erasableSyntaxOnly": true,
28
+ "noFallthroughCasesInSwitch": true,
29
+ "noUncheckedSideEffectImports": true
30
+ },
31
+ "include": ["src", "../biblio/**/*.tsx"]
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ],
7
+ "compilerOptions": {
8
+ "baseUrl": ".",
9
+ "paths": {
10
+ "@/*": [
11
+ "./src/*"
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "erasableSyntaxOnly": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedSideEffectImports": true
23
+ },
24
+ "include": ["vite.config.ts", "plugin/src/plugin.ts"]
25
+ }
package/vesl.config.ts ADDED
@@ -0,0 +1,4 @@
1
+
2
+ export default {
3
+ dir: 'test-content',
4
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,54 @@
1
+
2
+ import { defineConfig } from 'vite'
3
+ import react from '@vitejs/plugin-react'
4
+ import tailwindcss from '@tailwindcss/vite'
5
+ import mdx from '@mdx-js/rollup'
6
+ import remarkMath from 'remark-math'
7
+ import rehypeKatex from 'rehype-katex'
8
+ import remarkFrontmatter from 'remark-frontmatter'
9
+ import remarkGfm from 'remark-gfm'
10
+ import remarkMdxFrontmatter from 'remark-mdx-frontmatter'
11
+ import path from 'path'
12
+
13
+ export default defineConfig({
14
+ clearScreen: false,
15
+ publicDir: './public',
16
+ plugins: [
17
+ tailwindcss(),
18
+ // MDX must run before Vite's default transforms
19
+ {
20
+ enforce: 'pre',
21
+ ...mdx({
22
+ remarkPlugins: [
23
+ remarkGfm,
24
+ remarkMath,
25
+ remarkFrontmatter,
26
+ [remarkMdxFrontmatter, { name: 'frontmatter' }],
27
+ ],
28
+ rehypePlugins: [rehypeKatex],
29
+ providerImportSource: '@mdx-js/react',
30
+ }),
31
+ },
32
+ react({ include: /\.(jsx|js|mdx|md|tsx|ts)$/ }),
33
+ ],
34
+ resolve: {
35
+ alias: {
36
+ '@': path.resolve(__dirname, './src'),
37
+ 'react': path.resolve(__dirname, './node_modules/react'),
38
+ 'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
39
+ '@mdx-js/react': path.resolve(__dirname, './node_modules/@mdx-js/react'),
40
+ },
41
+ },
42
+ server: {
43
+ host: '0.0.0.0',
44
+ port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
45
+ strictPort: true,
46
+ fs: {
47
+ allow: ['..', '../..'],
48
+ },
49
+ allowedHosts: true,
50
+ },
51
+ preview: {
52
+ allowedHosts: true,
53
+ },
54
+ })