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.
- package/README.md +3 -0
- package/bin/lib/import-config.ts +13 -0
- package/bin/lib/init.ts +31 -0
- package/bin/lib/serve.ts +35 -0
- package/bin/lib/start.ts +40 -0
- package/bin/lib/stop.ts +24 -0
- package/bin/vesl.ts +41 -0
- package/components.json +20 -0
- package/eslint.config.js +23 -0
- package/index.html +17 -0
- package/package.json +89 -0
- package/plugin/README.md +21 -0
- package/plugin/package.json +26 -0
- package/plugin/src/cli.ts +30 -0
- package/plugin/src/client.tsx +224 -0
- package/plugin/src/lib.ts +268 -0
- package/plugin/src/plugin.ts +109 -0
- package/postcss.config.js +5 -0
- package/public/logo_dark.png +0 -0
- package/public/logo_light.png +0 -0
- package/src/App.tsx +21 -0
- package/src/components/front-matter.tsx +53 -0
- package/src/components/gallery/components/figure-caption.tsx +15 -0
- package/src/components/gallery/components/figure-header.tsx +20 -0
- package/src/components/gallery/components/lightbox.tsx +106 -0
- package/src/components/gallery/components/loading-image.tsx +48 -0
- package/src/components/gallery/hooks/use-gallery-images.ts +103 -0
- package/src/components/gallery/hooks/use-lightbox.ts +40 -0
- package/src/components/gallery/index.tsx +134 -0
- package/src/components/gallery/lib/render-math-in-text.tsx +47 -0
- package/src/components/header.tsx +68 -0
- package/src/components/index.ts +5 -0
- package/src/components/loading.tsx +16 -0
- package/src/components/mdx-components.tsx +163 -0
- package/src/components/mode-toggle.tsx +44 -0
- package/src/components/page-error.tsx +59 -0
- package/src/components/parameter-badge.tsx +78 -0
- package/src/components/parameter-table.tsx +420 -0
- package/src/components/post-list.tsx +148 -0
- package/src/components/running-bar.tsx +21 -0
- package/src/components/runtime-mdx.tsx +82 -0
- package/src/components/slide.tsx +11 -0
- package/src/components/theme-provider.tsx +6 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/breadcrumb.tsx +115 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +79 -0
- package/src/components/ui/carousel.tsx +260 -0
- package/src/components/ui/dropdown-menu.tsx +198 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/kbd.tsx +22 -0
- package/src/components/ui/select.tsx +158 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/shadcn-io/code-block/index.tsx +620 -0
- package/src/components/ui/shadcn-io/code-block/server.tsx +63 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/sidebar.tsx +771 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/components/welcome.tsx +21 -0
- package/src/hooks/use-key-bindings.ts +72 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/index.css +279 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/format-date.tsx +6 -0
- package/src/lib/format-file-size.ts +10 -0
- package/src/lib/parameter-utils.ts +134 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/src/pages/home.tsx +39 -0
- package/src/pages/post.tsx +65 -0
- package/src/pages/slides.tsx +173 -0
- package/tailwind.config.js +136 -0
- package/test-content/.vesl.json +49 -0
- package/test-content/README.md +33 -0
- package/test-content/test-post/README.mdx +7 -0
- package/test-content/test-slides/SLIDES.mdx +8 -0
- package/tsconfig.app.json +32 -0
- package/tsconfig.json +15 -0
- package/tsconfig.node.json +25 -0
- package/vesl.config.ts +4 -0
- 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,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,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
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
|
+
})
|