veslx 0.1.64 → 0.1.66
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 +0 -16
- package/bin/lib/serve.ts +46 -0
- package/dist/bin/lib/serve.js +36 -0
- package/dist/bin/veslx.js +0 -0
- package/dist/client/components/gallery/index.js +7 -23
- package/dist/client/components/gallery/index.js.map +1 -1
- package/dist/client/components/mdx-components.js +0 -14
- package/dist/client/components/mdx-components.js.map +1 -1
- package/dist/client/components/post-list-item.js +6 -5
- package/dist/client/components/post-list-item.js.map +1 -1
- package/dist/client/components/post-list.js +6 -1
- package/dist/client/components/post-list.js.map +1 -1
- package/dist/client/lib/content-classification.js +11 -2
- package/dist/client/lib/content-classification.js.map +1 -1
- package/dist/client/plugin/src/client.js +7 -40
- package/dist/client/plugin/src/client.js.map +1 -1
- package/dist/plugin/src/client.js +7 -0
- package/dist/plugin/src/plugin.js +5 -2
- package/package.json +1 -1
- package/plugin/src/client.tsx +10 -0
- package/plugin/src/plugin.ts +5 -2
- package/src/components/gallery/index.tsx +8 -23
- package/src/components/index.ts +0 -3
- package/src/components/mdx-components.tsx +0 -21
- package/src/components/post-list-item.tsx +10 -6
- package/src/components/post-list.tsx +8 -2
- package/src/lib/content-classification.ts +12 -2
- package/dist/client/components/parameter-badge.js +0 -48
- package/dist/client/components/parameter-badge.js.map +0 -1
- package/dist/client/components/parameter-table.js +0 -216
- package/dist/client/components/parameter-table.js.map +0 -1
- package/dist/client/components/slides/figure-slide.js +0 -14
- package/dist/client/components/slides/figure-slide.js.map +0 -1
- package/dist/client/components/slides/hero-slide.js +0 -21
- package/dist/client/components/slides/hero-slide.js.map +0 -1
- package/dist/client/components/slides/slide-outline.js +0 -28
- package/dist/client/components/slides/slide-outline.js.map +0 -1
- package/dist/client/components/slides/text-slide.js +0 -18
- package/dist/client/components/slides/text-slide.js.map +0 -1
- package/dist/client/components/veslx-cat.js +0 -40
- package/dist/client/components/veslx-cat.js.map +0 -1
- package/dist/client/lib/parameter-utils.js +0 -108
- package/dist/client/lib/parameter-utils.js.map +0 -1
- package/src/components/parameter-badge.tsx +0 -78
- package/src/components/parameter-table.tsx +0 -369
- package/src/components/slides/figure-slide.tsx +0 -16
- package/src/components/slides/hero-slide.tsx +0 -34
- package/src/components/slides/slide-outline.tsx +0 -38
- package/src/components/slides/text-slide.tsx +0 -35
- package/src/components/veslx-cat.tsx +0 -73
|
@@ -49,6 +49,11 @@ function findTsxFiles(directory) {
|
|
|
49
49
|
(child) => child.type === "file" && child.name.endsWith(".tsx") && !child.name.endsWith(".d.ts") && child.frontmatter !== void 0
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
+
function findPdfFiles(directory) {
|
|
53
|
+
return directory.children.filter(
|
|
54
|
+
(child) => child.type === "file" && child.name.toLowerCase().endsWith(".pdf")
|
|
55
|
+
);
|
|
56
|
+
}
|
|
52
57
|
function findSlides(directory) {
|
|
53
58
|
const standardSlides = directory.children.find(
|
|
54
59
|
(child) => child.type === "file" && [
|
|
@@ -97,44 +102,6 @@ function useDirectory(path = ".") {
|
|
|
97
102
|
error: result.error
|
|
98
103
|
};
|
|
99
104
|
}
|
|
100
|
-
function useFileContent(path) {
|
|
101
|
-
const [blob, setBlob] = useState(null);
|
|
102
|
-
const [content, setContent] = useState(null);
|
|
103
|
-
const [loading, setLoading] = useState(true);
|
|
104
|
-
const [error, setError] = useState(null);
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
const controller = new AbortController();
|
|
107
|
-
setLoading(true);
|
|
108
|
-
setError(null);
|
|
109
|
-
(async () => {
|
|
110
|
-
try {
|
|
111
|
-
const res = await fetch(`/raw/${path}`, {
|
|
112
|
-
signal: controller.signal
|
|
113
|
-
});
|
|
114
|
-
if (!res.ok) {
|
|
115
|
-
throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);
|
|
116
|
-
}
|
|
117
|
-
const fetchedBlob = await res.blob();
|
|
118
|
-
setBlob(fetchedBlob);
|
|
119
|
-
try {
|
|
120
|
-
const text = await fetchedBlob.text();
|
|
121
|
-
setContent(text);
|
|
122
|
-
} catch {
|
|
123
|
-
setContent(null);
|
|
124
|
-
}
|
|
125
|
-
} catch (err) {
|
|
126
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
setError(err instanceof Error ? err.message : "Unknown error");
|
|
130
|
-
} finally {
|
|
131
|
-
setLoading(false);
|
|
132
|
-
}
|
|
133
|
-
})();
|
|
134
|
-
return () => controller.abort();
|
|
135
|
-
}, [path]);
|
|
136
|
-
return { blob, content, loading, error };
|
|
137
|
-
}
|
|
138
105
|
function isSimulationRunning() {
|
|
139
106
|
const [running, setRunning] = useState(false);
|
|
140
107
|
useEffect(() => {
|
|
@@ -162,12 +129,12 @@ function isSimulationRunning() {
|
|
|
162
129
|
}
|
|
163
130
|
export {
|
|
164
131
|
findMdxFiles,
|
|
132
|
+
findPdfFiles,
|
|
165
133
|
findReadme,
|
|
166
134
|
findSlides,
|
|
167
135
|
findStandaloneSlides,
|
|
168
136
|
findTsxFiles,
|
|
169
137
|
isSimulationRunning,
|
|
170
|
-
useDirectory
|
|
171
|
-
useFileContent
|
|
138
|
+
useDirectory
|
|
172
139
|
};
|
|
173
140
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../plugin/src/client.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { DirectoryEntry, FileEntry } from \"./lib.js\";\nimport { buildDirectoryTree, navigateToPath } from \"./directory-tree.js\";\n// @ts-ignore - virtual module\nimport { files, frontmatters } from \"virtual:content-modules\";\n\n/**\n * Find the main content file for a directory.\n * Supports (in order of preference):\n * - index.mdx / index.md (modern convention)\n * - README.mdx / README.md (traditional convention)\n */\nexport function findReadme(directory: DirectoryEntry): FileEntry | null {\n const indexFiles = [\n \"index.mdx\", \"index.md\",\n \"README.mdx\", \"Readme.mdx\", \"readme.mdx\",\n \"README.md\", \"Readme.md\", \"readme.md\",\n ];\n\n for (const filename of indexFiles) {\n const found = directory.children.find((child) =>\n child.type === \"file\" && child.name === filename\n ) as FileEntry | undefined;\n if (found) return found;\n }\n\n return null;\n}\n\n/**\n * Find all MDX files in a directory (excluding index/README and slides)\n */\nexport function findMdxFiles(directory: DirectoryEntry): FileEntry[] {\n const indexFiles = [\n \"index.mdx\", \"index.md\",\n \"README.mdx\", \"Readme.mdx\", \"readme.mdx\",\n \"README.md\", \"Readme.md\", \"readme.md\",\n ];\n const slideFiles = [\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\",\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n ];\n const excludeFiles = [...indexFiles, ...slideFiles];\n\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n (child.name.endsWith('.mdx') || child.name.endsWith('.md')) &&\n !excludeFiles.includes(child.name) &&\n !child.name.endsWith('.slides.mdx') &&\n !child.name.endsWith('.slides.md')\n );\n}\n\n/**\n * Find TSX pages in a directory (requires frontmatter export).\n */\nexport function findTsxFiles(directory: DirectoryEntry): FileEntry[] {\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n child.name.endsWith('.tsx') &&\n !child.name.endsWith('.d.ts') &&\n child.frontmatter !== undefined\n );\n}\n\nexport function findSlides(directory: DirectoryEntry): FileEntry | null {\n // First check for standard SLIDES.mdx files\n const standardSlides = directory.children.find((child) =>\n child.type === \"file\" &&\n [\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\"\n ].includes(child.name)\n ) as FileEntry | undefined;\n\n if (standardSlides) return standardSlides;\n\n // Then check for *.slides.mdx files\n const dotSlides = directory.children.find((child) =>\n child.type === \"file\" &&\n (child.name.endsWith('.slides.mdx') || child.name.endsWith('.slides.md'))\n ) as FileEntry | undefined;\n\n return dotSlides || null;\n}\n\n/**\n * Find all standalone slides files in a directory (*.slides.mdx, *.slides.md)\n * These are slides files that aren't part of a folder (like getting-started.slides.mdx)\n */\nexport function findStandaloneSlides(directory: DirectoryEntry): FileEntry[] {\n const standardSlideFiles = [\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\",\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n ];\n\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n (child.name.endsWith('.slides.mdx') || child.name.endsWith('.slides.md')) &&\n !standardSlideFiles.includes(child.name)\n );\n}\n\n\nexport type DirectoryError =\n | { type: 'path_not_found'; message: string; status: 404 };\n\n// Build directory tree once from glob keys, with frontmatter metadata\nconst directoryTree = buildDirectoryTree(Object.keys(files), frontmatters as Record<string, FileEntry['frontmatter']>);\n\nexport function useDirectory(path: string = \".\") {\n const result = useMemo(() => {\n try {\n const { directory, file } = navigateToPath(directoryTree, path);\n return { directory, file, error: null as DirectoryError | null };\n } catch {\n return { directory: null, file: null, error: { type: 'path_not_found', message: `Path not found: ${path}`, status: 404 } };\n }\n }, [path]);\n\n return {\n directory: result.directory,\n file: result.file,\n loading: false, // No async loading needed\n error: result.error\n };\n}\n\nexport function useFileContent(path: string) {\n const [blob, setBlob] = useState<Blob | null>(null);\n const [content, setContent] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n setLoading(true);\n setError(null);\n\n (async () => {\n try {\n const res = await fetch(`/raw/${path}`, {\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);\n }\n\n const fetchedBlob = await res.blob();\n setBlob(fetchedBlob);\n\n // Try to read as text - some binary files may fail\n try {\n const text = await fetchedBlob.text();\n setContent(text);\n } catch {\n // Binary file - text content not available\n setContent(null);\n }\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n return; // Ignore abort errors\n }\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n })();\n\n return () => controller.abort();\n }, [path]);\n\n return { blob, content, loading, error };\n}\n\nexport function isSimulationRunning() {\n const [running, setRunning] = useState<boolean>(false);\n\n useEffect(() => {\n let interval: ReturnType<typeof setInterval>;\n\n const fetchStatus = async () => {\n try {\n const response = await fetch(`/raw/.running`);\n\n // this is an elaborate workaround to stop devtools logging errors on 404s\n const text = await response.text()\n if (text === \"\") {\n setRunning(true);\n } else {\n setRunning(false);\n }\n } catch {\n setRunning(false);\n }\n };\n\n // Initial fetch\n fetchStatus();\n\n // Poll every second\n interval = setInterval(fetchStatus, 1000);\n\n return () => {\n clearInterval(interval);\n };\n }, []);\n\n return running;\n}\n"],"names":[],"mappings":";;;AAYO,SAAS,WAAW,WAA6C;AACtE,QAAM,aAAa;AAAA,IACjB;AAAA,IAAa;AAAA,IACb;AAAA,IAAc;AAAA,IAAc;AAAA,IAC5B;AAAA,IAAa;AAAA,IAAa;AAAA,EAAA;AAG5B,aAAW,YAAY,YAAY;AACjC,UAAM,QAAQ,UAAU,SAAS;AAAA,MAAK,CAAC,UACrC,MAAM,SAAS,UAAU,MAAM,SAAS;AAAA,IAAA;AAE1C,QAAI,MAAO,QAAO;AAAA,EACpB;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,WAAwC;AACnE,QAAM,aAAa;AAAA,IACjB;AAAA,IAAa;AAAA,IACb;AAAA,IAAc;AAAA,IAAc;AAAA,IAC5B;AAAA,IAAa;AAAA,IAAa;AAAA,EAAA;AAE5B,QAAM,aAAa;AAAA,IACjB;AAAA,IAAc;AAAA,IAAc;AAAA,IAC5B;AAAA,IAAa;AAAA,IAAa;AAAA,EAAA;AAE5B,QAAM,eAAe,CAAC,GAAG,YAAY,GAAG,UAAU;AAElD,SAAO,UAAU,SAAS;AAAA,IAAO,CAAC,UAChC,MAAM,SAAS,WACd,MAAM,KAAK,SAAS,MAAM,KAAK,MAAM,KAAK,SAAS,KAAK,MACzD,CAAC,aAAa,SAAS,MAAM,IAAI,KACjC,CAAC,MAAM,KAAK,SAAS,aAAa,KAClC,CAAC,MAAM,KAAK,SAAS,YAAY;AAAA,EAAA;AAErC;AAKO,SAAS,aAAa,WAAwC;AACnE,SAAO,UAAU,SAAS;AAAA,IAAO,CAAC,UAChC,MAAM,SAAS,UACf,MAAM,KAAK,SAAS,MAAM,KAC1B,CAAC,MAAM,KAAK,SAAS,OAAO,KAC5B,MAAM,gBAAgB;AAAA,EAAA;AAE1B;AAEO,SAAS,WAAW,WAA6C;AAEtE,QAAM,iBAAiB,UAAU,SAAS;AAAA,IAAK,CAAC,UAC9C,MAAM,SAAS,UACf;AAAA,MACE;AAAA,MAAa;AAAA,MAAa;AAAA,MAC1B;AAAA,MAAc;AAAA,MAAc;AAAA,IAAA,EAC5B,SAAS,MAAM,IAAI;AAAA,EAAA;AAGvB,MAAI,eAAgB,QAAO;AAG3B,QAAM,YAAY,UAAU,SAAS;AAAA,IAAK,CAAC,UACzC,MAAM,SAAS,WACd,MAAM,KAAK,SAAS,aAAa,KAAK,MAAM,KAAK,SAAS,YAAY;AAAA,EAAA;AAGzE,SAAO,aAAa;AACtB;AAMO,SAAS,qBAAqB,WAAwC;AAC3E,QAAM,qBAAqB;AAAA,IACzB;AAAA,IAAc;AAAA,IAAc;AAAA,IAC5B;AAAA,IAAa;AAAA,IAAa;AAAA,EAAA;AAG5B,SAAO,UAAU,SAAS;AAAA,IAAO,CAAC,UAChC,MAAM,SAAS,WACd,MAAM,KAAK,SAAS,aAAa,KAAK,MAAM,KAAK,SAAS,YAAY,MACvE,CAAC,mBAAmB,SAAS,MAAM,IAAI;AAAA,EAAA;AAE3C;AAOA,MAAM,gBAAgB,mBAAmB,OAAO,KAAK,KAAK,GAAG,YAAwD;AAE9G,SAAS,aAAa,OAAe,KAAK;AAC/C,QAAM,SAAS,QAAQ,MAAM;AAC3B,QAAI;AACF,YAAM,EAAE,WAAW,KAAA,IAAS,eAAe,eAAe,IAAI;AAC9D,aAAO,EAAE,WAAW,MAAM,OAAO,KAAA;AAAA,IACnC,QAAQ;AACN,aAAO,EAAE,WAAW,MAAM,MAAM,MAAM,OAAO,EAAE,MAAM,kBAAkB,SAAS,mBAAmB,IAAI,IAAI,QAAQ,MAAI;AAAA,IACzH;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,SAAO;AAAA,IACL,WAAW,OAAO;AAAA,IAClB,MAAM,OAAO;AAAA,IACb,SAAS;AAAA;AAAA,IACT,OAAO,OAAO;AAAA,EAAA;AAElB;
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../plugin/src/client.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { DirectoryEntry, FileEntry } from \"./lib.js\";\nimport { buildDirectoryTree, navigateToPath } from \"./directory-tree.js\";\n// @ts-ignore - virtual module\nimport { files, frontmatters } from \"virtual:content-modules\";\n\n/**\n * Find the main content file for a directory.\n * Supports (in order of preference):\n * - index.mdx / index.md (modern convention)\n * - README.mdx / README.md (traditional convention)\n */\nexport function findReadme(directory: DirectoryEntry): FileEntry | null {\n const indexFiles = [\n \"index.mdx\", \"index.md\",\n \"README.mdx\", \"Readme.mdx\", \"readme.mdx\",\n \"README.md\", \"Readme.md\", \"readme.md\",\n ];\n\n for (const filename of indexFiles) {\n const found = directory.children.find((child) =>\n child.type === \"file\" && child.name === filename\n ) as FileEntry | undefined;\n if (found) return found;\n }\n\n return null;\n}\n\n/**\n * Find all MDX files in a directory (excluding index/README and slides)\n */\nexport function findMdxFiles(directory: DirectoryEntry): FileEntry[] {\n const indexFiles = [\n \"index.mdx\", \"index.md\",\n \"README.mdx\", \"Readme.mdx\", \"readme.mdx\",\n \"README.md\", \"Readme.md\", \"readme.md\",\n ];\n const slideFiles = [\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\",\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n ];\n const excludeFiles = [...indexFiles, ...slideFiles];\n\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n (child.name.endsWith('.mdx') || child.name.endsWith('.md')) &&\n !excludeFiles.includes(child.name) &&\n !child.name.endsWith('.slides.mdx') &&\n !child.name.endsWith('.slides.md')\n );\n}\n\n/**\n * Find TSX pages in a directory (requires frontmatter export).\n */\nexport function findTsxFiles(directory: DirectoryEntry): FileEntry[] {\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n child.name.endsWith('.tsx') &&\n !child.name.endsWith('.d.ts') &&\n child.frontmatter !== undefined\n );\n}\n\n/**\n * Find standalone PDF files in a directory.\n */\nexport function findPdfFiles(directory: DirectoryEntry): FileEntry[] {\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n child.name.toLowerCase().endsWith('.pdf')\n );\n}\n\nexport function findSlides(directory: DirectoryEntry): FileEntry | null {\n // First check for standard SLIDES.mdx files\n const standardSlides = directory.children.find((child) =>\n child.type === \"file\" &&\n [\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\"\n ].includes(child.name)\n ) as FileEntry | undefined;\n\n if (standardSlides) return standardSlides;\n\n // Then check for *.slides.mdx files\n const dotSlides = directory.children.find((child) =>\n child.type === \"file\" &&\n (child.name.endsWith('.slides.mdx') || child.name.endsWith('.slides.md'))\n ) as FileEntry | undefined;\n\n return dotSlides || null;\n}\n\n/**\n * Find all standalone slides files in a directory (*.slides.mdx, *.slides.md)\n * These are slides files that aren't part of a folder (like getting-started.slides.mdx)\n */\nexport function findStandaloneSlides(directory: DirectoryEntry): FileEntry[] {\n const standardSlideFiles = [\n \"SLIDES.mdx\", \"Slides.mdx\", \"slides.mdx\",\n \"SLIDES.md\", \"Slides.md\", \"slides.md\",\n ];\n\n return directory.children.filter((child): child is FileEntry =>\n child.type === \"file\" &&\n (child.name.endsWith('.slides.mdx') || child.name.endsWith('.slides.md')) &&\n !standardSlideFiles.includes(child.name)\n );\n}\n\n\nexport type DirectoryError =\n | { type: 'path_not_found'; message: string; status: 404 };\n\n// Build directory tree once from glob keys, with frontmatter metadata\nconst directoryTree = buildDirectoryTree(Object.keys(files), frontmatters as Record<string, FileEntry['frontmatter']>);\n\nexport function useDirectory(path: string = \".\") {\n const result = useMemo(() => {\n try {\n const { directory, file } = navigateToPath(directoryTree, path);\n return { directory, file, error: null as DirectoryError | null };\n } catch {\n return { directory: null, file: null, error: { type: 'path_not_found', message: `Path not found: ${path}`, status: 404 } };\n }\n }, [path]);\n\n return {\n directory: result.directory,\n file: result.file,\n loading: false, // No async loading needed\n error: result.error\n };\n}\n\nexport function useFileContent(path: string) {\n const [blob, setBlob] = useState<Blob | null>(null);\n const [content, setContent] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n setLoading(true);\n setError(null);\n\n (async () => {\n try {\n const res = await fetch(`/raw/${path}`, {\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);\n }\n\n const fetchedBlob = await res.blob();\n setBlob(fetchedBlob);\n\n // Try to read as text - some binary files may fail\n try {\n const text = await fetchedBlob.text();\n setContent(text);\n } catch {\n // Binary file - text content not available\n setContent(null);\n }\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n return; // Ignore abort errors\n }\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n })();\n\n return () => controller.abort();\n }, [path]);\n\n return { blob, content, loading, error };\n}\n\nexport function isSimulationRunning() {\n const [running, setRunning] = useState<boolean>(false);\n\n useEffect(() => {\n let interval: ReturnType<typeof setInterval>;\n\n const fetchStatus = async () => {\n try {\n const response = await fetch(`/raw/.running`);\n\n // this is an elaborate workaround to stop devtools logging errors on 404s\n const text = await response.text()\n if (text === \"\") {\n setRunning(true);\n } else {\n setRunning(false);\n }\n } catch {\n setRunning(false);\n }\n };\n\n // Initial fetch\n fetchStatus();\n\n // Poll every second\n interval = setInterval(fetchStatus, 1000);\n\n return () => {\n clearInterval(interval);\n };\n }, []);\n\n return running;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -50,6 +50,13 @@ export function findTsxFiles(directory) {
|
|
|
50
50
|
!child.name.endsWith('.d.ts') &&
|
|
51
51
|
child.frontmatter !== undefined);
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Find standalone PDF files in a directory.
|
|
55
|
+
*/
|
|
56
|
+
export function findPdfFiles(directory) {
|
|
57
|
+
return directory.children.filter((child) => child.type === "file" &&
|
|
58
|
+
child.name.toLowerCase().endsWith('.pdf'));
|
|
59
|
+
}
|
|
53
60
|
export function findSlides(directory) {
|
|
54
61
|
// First check for standard SLIDES.mdx files
|
|
55
62
|
const standardSlides = directory.children.find((child) => child.type === "file" &&
|
|
@@ -514,6 +514,7 @@ export default function contentPlugin(contentDir, config, options) {
|
|
|
514
514
|
'.jpeg': 'image/jpeg',
|
|
515
515
|
'.gif': 'image/gif',
|
|
516
516
|
'.svg': 'image/svg+xml',
|
|
517
|
+
'.pdf': 'application/pdf',
|
|
517
518
|
'.json': 'application/json',
|
|
518
519
|
'.md': 'text/markdown',
|
|
519
520
|
'.yaml': 'text/yaml',
|
|
@@ -612,6 +613,7 @@ export const files = import.meta.glob([
|
|
|
612
613
|
'@content/*.gif',
|
|
613
614
|
'@content/*.svg',
|
|
614
615
|
'@content/*.webp',
|
|
616
|
+
'@content/*.pdf',
|
|
615
617
|
'@content/*.css',
|
|
616
618
|
'@content/*.yaml',
|
|
617
619
|
'@content/*.yml',
|
|
@@ -628,6 +630,7 @@ export const files = import.meta.glob([
|
|
|
628
630
|
'@content/**/*.gif',
|
|
629
631
|
'@content/**/*.svg',
|
|
630
632
|
'@content/**/*.webp',
|
|
633
|
+
'@content/**/*.pdf',
|
|
631
634
|
'@content/**/*.css',
|
|
632
635
|
'@content/**/*.yaml',
|
|
633
636
|
'@content/**/*.yml',
|
|
@@ -659,7 +662,7 @@ export const modules = import.meta.glob(['@content/*.mdx', '@content/*.md', '@co
|
|
|
659
662
|
// Watch content directory for all file changes (add, delete, change)
|
|
660
663
|
server.watcher.add(dir);
|
|
661
664
|
// File extensions that should trigger a full reload
|
|
662
|
-
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css'];
|
|
665
|
+
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.pdf', '.tsx', '.ts', '.jsx', '.js', '.css'];
|
|
663
666
|
// Debounce reload to avoid rapid-fire refreshes when multiple files change
|
|
664
667
|
let reloadTimeout = null;
|
|
665
668
|
const pendingChanges = [];
|
|
@@ -719,7 +722,7 @@ export const modules = import.meta.glob(['@content/*.mdx', '@content/*.md', '@co
|
|
|
719
722
|
// Check if the changed file is in the content directory
|
|
720
723
|
// Return empty array to prevent default HMR - we handle it in configureServer
|
|
721
724
|
if (file.startsWith(dir)) {
|
|
722
|
-
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css'];
|
|
725
|
+
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.pdf', '.tsx', '.ts', '.jsx', '.js', '.css'];
|
|
723
726
|
const ext = path.extname(file).toLowerCase();
|
|
724
727
|
if (watchedExtensions.includes(ext)) {
|
|
725
728
|
return []; // Prevent default HMR, we already handle this via watcher events
|
package/package.json
CHANGED
package/plugin/src/client.tsx
CHANGED
|
@@ -63,6 +63,16 @@ export function findTsxFiles(directory: DirectoryEntry): FileEntry[] {
|
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Find standalone PDF files in a directory.
|
|
68
|
+
*/
|
|
69
|
+
export function findPdfFiles(directory: DirectoryEntry): FileEntry[] {
|
|
70
|
+
return directory.children.filter((child): child is FileEntry =>
|
|
71
|
+
child.type === "file" &&
|
|
72
|
+
child.name.toLowerCase().endsWith('.pdf')
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
export function findSlides(directory: DirectoryEntry): FileEntry | null {
|
|
67
77
|
// First check for standard SLIDES.mdx files
|
|
68
78
|
const standardSlides = directory.children.find((child) =>
|
package/plugin/src/plugin.ts
CHANGED
|
@@ -580,6 +580,7 @@ export default function contentPlugin(contentDir: string, config?: VeslxConfig,
|
|
|
580
580
|
'.jpeg': 'image/jpeg',
|
|
581
581
|
'.gif': 'image/gif',
|
|
582
582
|
'.svg': 'image/svg+xml',
|
|
583
|
+
'.pdf': 'application/pdf',
|
|
583
584
|
'.json': 'application/json',
|
|
584
585
|
'.md': 'text/markdown',
|
|
585
586
|
'.yaml': 'text/yaml',
|
|
@@ -692,6 +693,7 @@ export const files = import.meta.glob([
|
|
|
692
693
|
'@content/*.gif',
|
|
693
694
|
'@content/*.svg',
|
|
694
695
|
'@content/*.webp',
|
|
696
|
+
'@content/*.pdf',
|
|
695
697
|
'@content/*.css',
|
|
696
698
|
'@content/*.yaml',
|
|
697
699
|
'@content/*.yml',
|
|
@@ -708,6 +710,7 @@ export const files = import.meta.glob([
|
|
|
708
710
|
'@content/**/*.gif',
|
|
709
711
|
'@content/**/*.svg',
|
|
710
712
|
'@content/**/*.webp',
|
|
713
|
+
'@content/**/*.pdf',
|
|
711
714
|
'@content/**/*.css',
|
|
712
715
|
'@content/**/*.yaml',
|
|
713
716
|
'@content/**/*.yml',
|
|
@@ -743,7 +746,7 @@ export const modules = import.meta.glob(['@content/*.mdx', '@content/*.md', '@co
|
|
|
743
746
|
server.watcher.add(dir)
|
|
744
747
|
|
|
745
748
|
// File extensions that should trigger a full reload
|
|
746
|
-
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css']
|
|
749
|
+
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.pdf', '.tsx', '.ts', '.jsx', '.js', '.css']
|
|
747
750
|
|
|
748
751
|
// Debounce reload to avoid rapid-fire refreshes when multiple files change
|
|
749
752
|
let reloadTimeout: ReturnType<typeof setTimeout> | null = null
|
|
@@ -813,7 +816,7 @@ export const modules = import.meta.glob(['@content/*.mdx', '@content/*.md', '@co
|
|
|
813
816
|
// Check if the changed file is in the content directory
|
|
814
817
|
// Return empty array to prevent default HMR - we handle it in configureServer
|
|
815
818
|
if (file.startsWith(dir)) {
|
|
816
|
-
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css']
|
|
819
|
+
const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.pdf', '.tsx', '.ts', '.jsx', '.js', '.css']
|
|
817
820
|
const ext = path.extname(file).toLowerCase()
|
|
818
821
|
if (watchedExtensions.includes(ext)) {
|
|
819
822
|
return [] // Prevent default HMR, we already handle this via watcher events
|
|
@@ -124,7 +124,7 @@ export default function Gallery({
|
|
|
124
124
|
{[...Array(3)].map((_, i) => (
|
|
125
125
|
<div
|
|
126
126
|
key={i}
|
|
127
|
-
className="
|
|
127
|
+
className="h-40 rounded-sm bg-muted/20 relative overflow-hidden"
|
|
128
128
|
>
|
|
129
129
|
<div
|
|
130
130
|
className="absolute inset-0 bg-gradient-to-r from-transparent via-muted/30 to-transparent animate-shimmer"
|
|
@@ -168,13 +168,13 @@ export default function Gallery({
|
|
|
168
168
|
<div
|
|
169
169
|
key={index}
|
|
170
170
|
title={img.label}
|
|
171
|
-
className={`
|
|
171
|
+
className={`overflow-hidden rounded-sm bg-muted/10 cursor-pointer group ${className || ''}`}
|
|
172
172
|
onClick={() => lightbox.open(index)}
|
|
173
173
|
>
|
|
174
174
|
<LoadingImage
|
|
175
175
|
src={img.src}
|
|
176
176
|
alt={img.label}
|
|
177
|
-
className="object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]"
|
|
177
|
+
className="w-full h-auto object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]"
|
|
178
178
|
/>
|
|
179
179
|
</div>
|
|
180
180
|
);
|
|
@@ -212,11 +212,6 @@ export default function Gallery({
|
|
|
212
212
|
const offset = rowsToRender.slice(0, rowIndex).reduce((acc, row) => acc + row.length, 0);
|
|
213
213
|
const rowSubtitle = subtitles?.[rowIndex];
|
|
214
214
|
const rowWrapperClass = "max-w-[var(--gallery-width)] w-full mx-auto";
|
|
215
|
-
const placeholders = Math.max(0, maxRowColumns - rowImages.length);
|
|
216
|
-
const rowCells = [
|
|
217
|
-
...rowImages.map((img, index) => ({ img, index })),
|
|
218
|
-
...Array.from({ length: placeholders }, () => ({ img: null, index: -1 })),
|
|
219
|
-
];
|
|
220
215
|
|
|
221
216
|
return (
|
|
222
217
|
<div key={`${rowIndex}-${rowPaths.join("|")}`}>
|
|
@@ -225,17 +220,7 @@ export default function Gallery({
|
|
|
225
220
|
className="grid gap-3"
|
|
226
221
|
style={{ gridTemplateColumns: `repeat(${Math.max(1, maxRowColumns)}, minmax(0, 1fr))` }}
|
|
227
222
|
>
|
|
228
|
-
{
|
|
229
|
-
if (!cell.img) {
|
|
230
|
-
return (
|
|
231
|
-
<div
|
|
232
|
-
key={`empty-${rowIndex}-${index}`}
|
|
233
|
-
className="aspect-square rounded-sm opacity-0 pointer-events-none"
|
|
234
|
-
/>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
return imageElement(offset + cell.index, cell.img, 'w-full');
|
|
238
|
-
})}
|
|
223
|
+
{rowImages.map((img, index) => imageElement(offset + index, img, 'w-full'))}
|
|
239
224
|
</div>
|
|
240
225
|
</div>
|
|
241
226
|
|
|
@@ -255,22 +240,22 @@ export default function Gallery({
|
|
|
255
240
|
<FigureCaption caption={caption} label={captionLabel} />
|
|
256
241
|
</div>
|
|
257
242
|
) : isCompact ? (
|
|
258
|
-
<div className="flex gap-3">
|
|
243
|
+
<div className="flex items-start gap-3">
|
|
259
244
|
{images.map((img, index) => imageElement(index, img, 'flex-1'))}
|
|
260
245
|
</div>
|
|
261
246
|
) : (
|
|
262
|
-
<div ref={scrollRef} className="gallery-scroll-row flex gap-3 overflow-x-auto overscroll-x-contain pb-4">
|
|
247
|
+
<div ref={scrollRef} className="gallery-scroll-row flex items-start gap-3 overflow-x-auto overscroll-x-contain pb-4">
|
|
263
248
|
{images.map((img, index) => (
|
|
264
249
|
<div
|
|
265
250
|
key={index}
|
|
266
251
|
title={img.label}
|
|
267
|
-
className="flex-none w-[calc(var(--gallery-width)*0.3)] min-w-[250px]
|
|
252
|
+
className="flex-none w-[calc(var(--gallery-width)*0.3)] min-w-[250px] overflow-hidden rounded-sm bg-muted/10 cursor-pointer group"
|
|
268
253
|
onClick={() => lightbox.open(index)}
|
|
269
254
|
>
|
|
270
255
|
<LoadingImage
|
|
271
256
|
src={img.src}
|
|
272
257
|
alt={img.label}
|
|
273
|
-
className="object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]"
|
|
258
|
+
className="w-full h-auto object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]"
|
|
274
259
|
/>
|
|
275
260
|
</div>
|
|
276
261
|
))}
|
package/src/components/index.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
|
|
2
2
|
export { default as Gallery } from './gallery'
|
|
3
|
-
export { ParameterTable } from './parameter-table'
|
|
4
|
-
export { ParameterBadge } from './parameter-badge'
|
|
5
3
|
export { Slide } from './slide'
|
|
6
|
-
export { VeslxCat } from './veslx-cat'
|
|
7
4
|
export { VeslxTOC } from './veslx-toc'
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { Link, useLocation } from 'react-router-dom'
|
|
2
2
|
import Gallery from '@/components/gallery'
|
|
3
|
-
import { ParameterTable } from '@/components/parameter-table'
|
|
4
|
-
import { ParameterBadge } from '@/components/parameter-badge'
|
|
5
|
-
import { VeslxCat } from '@/components/veslx-cat'
|
|
6
3
|
import { VeslxTOC } from '@/components/veslx-toc'
|
|
7
4
|
import { FrontMatter } from './front-matter'
|
|
8
|
-
import { HeroSlide } from './slides/hero-slide'
|
|
9
|
-
import { FigureSlide } from './slides/figure-slide'
|
|
10
|
-
import { TextSlide } from './slides/text-slide'
|
|
11
|
-
import { SlideOutline } from './slides/slide-outline'
|
|
12
5
|
import { PostList } from '@/components/post-list'
|
|
13
6
|
import { PostListItem } from '@/components/post-list-item'
|
|
14
7
|
import { VeslxSearch } from '@/components/veslx-search'
|
|
@@ -83,24 +76,10 @@ export const mdxComponents = {
|
|
|
83
76
|
|
|
84
77
|
VeslxGallery: Gallery,
|
|
85
78
|
|
|
86
|
-
VeslxParameterTable: ParameterTable,
|
|
87
|
-
|
|
88
|
-
VeslxParameterBadge: ParameterBadge,
|
|
89
|
-
|
|
90
|
-
VeslxHeroSlide: HeroSlide,
|
|
91
|
-
|
|
92
|
-
VeslxFigureSlide: FigureSlide,
|
|
93
|
-
|
|
94
|
-
VeslxTextSlide: TextSlide,
|
|
95
|
-
|
|
96
|
-
VeslxSlideOutline: SlideOutline,
|
|
97
|
-
|
|
98
79
|
VeslxPostList: PostList,
|
|
99
80
|
|
|
100
81
|
VeslxPostListItem: PostListItem,
|
|
101
82
|
|
|
102
|
-
VeslxCat: VeslxCat,
|
|
103
|
-
|
|
104
83
|
VeslxTOC: VeslxTOC,
|
|
105
84
|
|
|
106
85
|
VeslxSearch: VeslxSearch,
|
|
@@ -7,12 +7,16 @@ interface PostListItemProps {
|
|
|
7
7
|
title: string;
|
|
8
8
|
description?: string;
|
|
9
9
|
date?: Date;
|
|
10
|
-
href
|
|
10
|
+
href?: string;
|
|
11
|
+
/** Alias for href (used in MDX as linkPath) */
|
|
12
|
+
linkPath?: string;
|
|
11
13
|
external?: boolean;
|
|
14
|
+
openInNewTab?: boolean;
|
|
12
15
|
isSlides?: boolean;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export function PostListItem({ title, description, date, href, external, isSlides }: PostListItemProps) {
|
|
18
|
+
export function PostListItem({ title, description, date, href, linkPath, external, openInNewTab = true, isSlides }: PostListItemProps) {
|
|
19
|
+
const resolvedHref = href || linkPath || '#';
|
|
16
20
|
const className = cn(
|
|
17
21
|
"group block py-3 px-3 -mx-3 rounded-md",
|
|
18
22
|
"transition-colors duration-150",
|
|
@@ -53,9 +57,9 @@ export function PostListItem({ title, description, date, href, external, isSlide
|
|
|
53
57
|
if (external) {
|
|
54
58
|
return (
|
|
55
59
|
<a
|
|
56
|
-
href={
|
|
57
|
-
target="_blank"
|
|
58
|
-
rel="noopener noreferrer"
|
|
60
|
+
href={resolvedHref}
|
|
61
|
+
target={openInNewTab ? "_blank" : undefined}
|
|
62
|
+
rel={openInNewTab ? "noopener noreferrer" : undefined}
|
|
59
63
|
className={className}
|
|
60
64
|
>
|
|
61
65
|
{content}
|
|
@@ -65,7 +69,7 @@ export function PostListItem({ title, description, date, href, external, isSlide
|
|
|
65
69
|
|
|
66
70
|
return (
|
|
67
71
|
<Link
|
|
68
|
-
to={
|
|
72
|
+
to={resolvedHref}
|
|
69
73
|
className={className}
|
|
70
74
|
>
|
|
71
75
|
{content}
|
|
@@ -36,7 +36,11 @@ function filePathToRoutePath(filePath: string): string {
|
|
|
36
36
|
// Helper to get link path from post
|
|
37
37
|
function getLinkPath(post: PostEntry): string {
|
|
38
38
|
if (post.file) {
|
|
39
|
-
// Standalone
|
|
39
|
+
// Standalone PDF file should open directly.
|
|
40
|
+
if (post.file.path.toLowerCase().endsWith('.pdf')) {
|
|
41
|
+
return `/raw/${post.file.path}`;
|
|
42
|
+
}
|
|
43
|
+
// Standalone MDX/TSX file
|
|
40
44
|
return filePathToRoutePath(post.file.path);
|
|
41
45
|
} else if (post.slides && !post.readme) {
|
|
42
46
|
// Folder with only slides
|
|
@@ -51,7 +55,7 @@ function getLinkPath(post: PostEntry): string {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
function isRouterPath(href: string): boolean {
|
|
54
|
-
return href.startsWith("/") && !href.startsWith("//");
|
|
58
|
+
return href.startsWith("/") && !href.startsWith("//") && !href.startsWith("/raw/");
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
export function PostList({ globs = null }: PostListProps) {
|
|
@@ -143,6 +147,7 @@ export function PostList({ globs = null }: PostListProps) {
|
|
|
143
147
|
typeof frontmatter?.link === "string" ? frontmatter.link.trim() : "";
|
|
144
148
|
const href = frontmatterLink || internalLinkPath;
|
|
145
149
|
const external = !isRouterPath(href);
|
|
150
|
+
const openInNewTab = external && !href.startsWith("/raw/");
|
|
146
151
|
|
|
147
152
|
return (
|
|
148
153
|
<PostListItem
|
|
@@ -152,6 +157,7 @@ export function PostList({ globs = null }: PostListProps) {
|
|
|
152
157
|
date={date}
|
|
153
158
|
href={href}
|
|
154
159
|
external={external}
|
|
160
|
+
openInNewTab={openInNewTab}
|
|
155
161
|
isSlides={isSlides}
|
|
156
162
|
/>
|
|
157
163
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DirectoryEntry, FileEntry } from "../../plugin/src/lib";
|
|
2
|
-
import { findReadme, findSlides, findMdxFiles, findStandaloneSlides, findTsxFiles } from "../../plugin/src/client";
|
|
2
|
+
import { findReadme, findSlides, findMdxFiles, findStandaloneSlides, findTsxFiles, findPdfFiles } from "../../plugin/src/client";
|
|
3
3
|
|
|
4
4
|
export type PostEntry = {
|
|
5
5
|
type: 'folder' | 'file';
|
|
@@ -19,6 +19,7 @@ export function directoryToPostEntries(directory: DirectoryEntry): PostEntry[] {
|
|
|
19
19
|
const standaloneFiles = findMdxFiles(directory);
|
|
20
20
|
const standaloneTsxFiles = findTsxFiles(directory);
|
|
21
21
|
const standaloneSlidesFiles = findStandaloneSlides(directory);
|
|
22
|
+
const standalonePdfFiles = findPdfFiles(directory);
|
|
22
23
|
|
|
23
24
|
const folderPosts: PostEntry[] = folders
|
|
24
25
|
.map((folder) => ({
|
|
@@ -59,7 +60,16 @@ export function directoryToPostEntries(directory: DirectoryEntry): PostEntry[] {
|
|
|
59
60
|
file: null,
|
|
60
61
|
}));
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
const pdfPosts: PostEntry[] = standalonePdfFiles.map((file) => ({
|
|
64
|
+
type: 'file' as const,
|
|
65
|
+
name: file.name.replace(/\.pdf$/i, ''),
|
|
66
|
+
path: file.path,
|
|
67
|
+
readme: null,
|
|
68
|
+
slides: null,
|
|
69
|
+
file,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
return [...folderPosts, ...filePosts, ...tsxPosts, ...slidesPosts, ...pdfPosts];
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
export function filterVisiblePosts(posts: PostEntry[]): PostEntry[] {
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useFileContent } from "../plugin/src/client.js";
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { cn } from "../lib/utils.js";
|
|
5
|
-
import { parseConfigFile, extractPath, deriveLabelFromPath, formatValue, getValueType } from "../lib/parameter-utils.js";
|
|
6
|
-
function ParameterBadge({ path, keyPath, label, unit }) {
|
|
7
|
-
const { content, loading, error } = useFileContent(path);
|
|
8
|
-
const { value, displayLabel } = useMemo(() => {
|
|
9
|
-
if (!content) return { value: void 0, displayLabel: "" };
|
|
10
|
-
const data = parseConfigFile(content, path);
|
|
11
|
-
if (!data) return { value: void 0, displayLabel: "" };
|
|
12
|
-
const extracted = extractPath(data, keyPath);
|
|
13
|
-
const derivedLabel = label || deriveLabelFromPath(keyPath);
|
|
14
|
-
return { value: extracted, displayLabel: derivedLabel };
|
|
15
|
-
}, [content, path, keyPath, label]);
|
|
16
|
-
if (loading) {
|
|
17
|
-
return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-muted/50 border border-border/50", children: /* @__PURE__ */ jsx("span", { className: "w-2 h-2 border border-muted-foreground/40 border-t-transparent rounded-full animate-spin" }) });
|
|
18
|
-
}
|
|
19
|
-
if (error || value === void 0) {
|
|
20
|
-
return /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-destructive/10 border border-destructive/30", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-destructive", children: "—" }) });
|
|
21
|
-
}
|
|
22
|
-
const type = getValueType(value);
|
|
23
|
-
const formattedValue = formatValue(value);
|
|
24
|
-
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md font-mono text-[11px]", children: [
|
|
25
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: displayLabel }),
|
|
26
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/40", children: "=" }),
|
|
27
|
-
/* @__PURE__ */ jsx(
|
|
28
|
-
"span",
|
|
29
|
-
{
|
|
30
|
-
className: cn(
|
|
31
|
-
"font-medium tabular-nums",
|
|
32
|
-
type === "number" && "text-foreground",
|
|
33
|
-
type === "string" && "text-amber-600 dark:text-amber-500",
|
|
34
|
-
type === "boolean" && "text-cyan-600 dark:text-cyan-500",
|
|
35
|
-
type === "null" && "text-muted-foreground/50",
|
|
36
|
-
type === "array" && "text-purple-600 dark:text-purple-400",
|
|
37
|
-
type === "object" && "text-purple-600 dark:text-purple-400"
|
|
38
|
-
),
|
|
39
|
-
children: type === "string" ? `"${formattedValue}"` : formattedValue
|
|
40
|
-
}
|
|
41
|
-
),
|
|
42
|
-
unit && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60", children: unit })
|
|
43
|
-
] });
|
|
44
|
-
}
|
|
45
|
-
export {
|
|
46
|
-
ParameterBadge
|
|
47
|
-
};
|
|
48
|
-
//# sourceMappingURL=parameter-badge.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parameter-badge.js","sources":["../../../src/components/parameter-badge.tsx"],"sourcesContent":["import { useFileContent } from \"../../plugin/src/client\";\nimport { useMemo } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n type ParameterValue,\n extractPath,\n getValueType,\n formatValue,\n parseConfigFile,\n deriveLabelFromPath,\n} from \"@/lib/parameter-utils\";\n\ninterface ParameterBadgeProps {\n /** Path to the YAML or JSON file */\n path: string;\n /** jq-like path to the value (e.g., \".base.N_E\") */\n keyPath: string;\n /** Optional label override (defaults to last segment of keyPath) */\n label?: string;\n /** Optional unit suffix (e.g., \"ms\", \"Hz\") */\n unit?: string;\n}\n\nexport function ParameterBadge({ path, keyPath, label, unit }: ParameterBadgeProps) {\n const { content, loading, error } = useFileContent(path);\n\n const { value, displayLabel } = useMemo(() => {\n if (!content) return { value: undefined, displayLabel: \"\" };\n\n const data = parseConfigFile(content, path);\n if (!data) return { value: undefined, displayLabel: \"\" };\n\n const extracted = extractPath(data, keyPath);\n const derivedLabel = label || deriveLabelFromPath(keyPath);\n\n return { value: extracted, displayLabel: derivedLabel };\n }, [content, path, keyPath, label]);\n\n if (loading) {\n return (\n <span className=\"inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-muted/50 border border-border/50\">\n <span className=\"w-2 h-2 border border-muted-foreground/40 border-t-transparent rounded-full animate-spin\" />\n </span>\n );\n }\n\n if (error || value === undefined) {\n return (\n <span className=\"inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-destructive/10 border border-destructive/30\">\n <span className=\"text-[10px] font-mono text-destructive\">—</span>\n </span>\n );\n }\n\n const type = getValueType(value);\n const formattedValue = formatValue(value);\n\n return (\n <span className=\"inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md font-mono text-[11px]\">\n <span className=\"text-muted-foreground\">{displayLabel}</span>\n <span className=\"text-muted-foreground/40\">=</span>\n <span\n className={cn(\n \"font-medium tabular-nums\",\n type === \"number\" && \"text-foreground\",\n type === \"string\" && \"text-amber-600 dark:text-amber-500\",\n type === \"boolean\" && \"text-cyan-600 dark:text-cyan-500\",\n type === \"null\" && \"text-muted-foreground/50\",\n type === \"array\" && \"text-purple-600 dark:text-purple-400\",\n type === \"object\" && \"text-purple-600 dark:text-purple-400\"\n )}\n >\n {type === \"string\" ? `\"${formattedValue}\"` : formattedValue}\n </span>\n {unit && <span className=\"text-muted-foreground/60\">{unit}</span>}\n </span>\n );\n}\n"],"names":[],"mappings":";;;;;AAuBO,SAAS,eAAe,EAAE,MAAM,SAAS,OAAO,QAA6B;AAClF,QAAM,EAAE,SAAS,SAAS,MAAA,IAAU,eAAe,IAAI;AAEvD,QAAM,EAAE,OAAO,aAAA,IAAiB,QAAQ,MAAM;AAC5C,QAAI,CAAC,QAAS,QAAO,EAAE,OAAO,QAAW,cAAc,GAAA;AAEvD,UAAM,OAAO,gBAAgB,SAAS,IAAI;AAC1C,QAAI,CAAC,KAAM,QAAO,EAAE,OAAO,QAAW,cAAc,GAAA;AAEpD,UAAM,YAAY,YAAY,MAAM,OAAO;AAC3C,UAAM,eAAe,SAAS,oBAAoB,OAAO;AAEzD,WAAO,EAAE,OAAO,WAAW,cAAc,aAAA;AAAA,EAC3C,GAAG,CAAC,SAAS,MAAM,SAAS,KAAK,CAAC;AAElC,MAAI,SAAS;AACX,WACE,oBAAC,UAAK,WAAU,+FACd,8BAAC,QAAA,EAAK,WAAU,4FAA2F,EAAA,CAC7G;AAAA,EAEJ;AAEA,MAAI,SAAS,UAAU,QAAW;AAChC,WACE,oBAAC,UAAK,WAAU,0GACd,8BAAC,QAAA,EAAK,WAAU,0CAAyC,UAAA,IAAA,CAAC,EAAA,CAC5D;AAAA,EAEJ;AAEA,QAAM,OAAO,aAAa,KAAK;AAC/B,QAAM,iBAAiB,YAAY,KAAK;AAExC,SACE,qBAAC,QAAA,EAAK,WAAU,iFACd,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,yBAAyB,UAAA,cAAa;AAAA,IACtD,oBAAC,QAAA,EAAK,WAAU,4BAA2B,UAAA,KAAC;AAAA,IAC5C;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SAAS,YAAY;AAAA,UACrB,SAAS,YAAY;AAAA,UACrB,SAAS,aAAa;AAAA,UACtB,SAAS,UAAU;AAAA,UACnB,SAAS,WAAW;AAAA,UACpB,SAAS,YAAY;AAAA,QAAA;AAAA,QAGtB,UAAA,SAAS,WAAW,IAAI,cAAc,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,IAE9C,QAAQ,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,KAAA,CAAK;AAAA,EAAA,GAC5D;AAEJ;"}
|