veslx 0.1.53 → 0.1.55

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/bin/lib/init.ts CHANGED
@@ -5,13 +5,6 @@ import readline from "node:readline/promises";
5
5
  import process from "node:process";
6
6
 
7
7
  export default async function createNewConfig() {
8
- const configPath = "veslx.yaml";
9
-
10
- if (fs.existsSync(configPath)) {
11
- console.error(`Configuration file '${configPath}' already exists.`);
12
- return;
13
- }
14
-
15
8
  const cwd = process.cwd();
16
9
  const folderName = nodePath.basename(cwd);
17
10
 
@@ -37,6 +30,12 @@ export default async function createNewConfig() {
37
30
  ? contentDir
38
31
  : nodePath.resolve(cwd, contentDir);
39
32
 
33
+ const configPath = nodePath.join(resolvedContentDir, "veslx.yaml");
34
+ if (fs.existsSync(configPath)) {
35
+ console.error(`Configuration file '${nodePath.relative(cwd, configPath) || "veslx.yaml"}' already exists.`);
36
+ return;
37
+ }
38
+
40
39
  if (!fs.existsSync(resolvedContentDir)) {
41
40
  await fs.promises.mkdir(resolvedContentDir, { recursive: true });
42
41
  console.log(`Created content directory: ${contentDir}`);
@@ -48,16 +47,29 @@ export default async function createNewConfig() {
48
47
  console.log(`Skipped homepage: ${nodePath.relative(cwd, indexPath) || "index.mdx"} already exists.`);
49
48
  } else {
50
49
  const safeTitle = folderName.replace(/"/g, '\\"');
51
- const homepage = `---\ntitle: "${safeTitle}"\n---\n\n<VeslxFrontMatter />\n\n# ${folderName}\n\n<VeslxPostList />\n`;
50
+ const homepage = `---\ntitle: "Welcome to ${safeTitle}"\n---\n\n<VeslxFrontMatter />\n\n# Welcome to ${safeTitle}\n\nThis is your new veslx site. Start by editing posts or adding new folders.\n\n<VeslxPostList />\n`;
52
51
  await fs.promises.writeFile(indexPath, homepage, "utf-8");
53
52
  console.log(`Created custom homepage: ${nodePath.relative(cwd, indexPath) || "index.mdx"}`);
54
53
  }
55
54
  }
56
55
 
56
+ const sampleDir = nodePath.join(resolvedContentDir, "getting-started");
57
+ const samplePath = nodePath.join(sampleDir, "README.mdx");
58
+ if (fs.existsSync(samplePath)) {
59
+ console.log(`Skipped sample post: ${nodePath.relative(cwd, samplePath)}`);
60
+ } else {
61
+ await fs.promises.mkdir(sampleDir, { recursive: true });
62
+ const safeTitle = folderName.replace(/"/g, '\\"');
63
+ const samplePost = `---\ntitle: "Getting Started"\ndate: "${new Date().toISOString().slice(0, 10)}"\ndescription: "Your first veslx post"\n---\n\n# Getting Started\n\nWelcome to **${safeTitle}**. This is a sample post you can edit or delete.\n\n- Write in MDX\n- Add images under \`./images\`\n- Create slides with \`SLIDES.mdx\`\n\n`;
64
+ await fs.promises.writeFile(samplePath, samplePost, "utf-8");
65
+ console.log(`Created sample post: ${nodePath.relative(cwd, samplePath)}`);
66
+ }
67
+
57
68
  const config = {
58
69
  dir: contentDir,
59
70
  site: {
60
- name: folderName,
71
+ name: "veslx",
72
+ description: "my new veslx site",
61
73
  github: "",
62
74
  },
63
75
  };
@@ -66,7 +78,7 @@ export default async function createNewConfig() {
66
78
 
67
79
  await fs.promises.writeFile(configPath, configStr, "utf-8");
68
80
 
69
- console.log(`Created veslx.yaml`);
81
+ console.log(`Created ${nodePath.relative(cwd, configPath) || "veslx.yaml"}`);
70
82
  console.log(`\nEdit the file to customize your site, then run:`);
71
- console.log(` bunx veslx serve`);
83
+ console.log(` bunx veslx serve ${contentDir}`);
72
84
  }
@@ -4,11 +4,6 @@ import yaml from "js-yaml";
4
4
  import readline from "node:readline/promises";
5
5
  import process from "node:process";
6
6
  export default async function createNewConfig() {
7
- const configPath = "veslx.yaml";
8
- if (fs.existsSync(configPath)) {
9
- console.error(`Configuration file '${configPath}' already exists.`);
10
- return;
11
- }
12
7
  const cwd = process.cwd();
13
8
  const folderName = nodePath.basename(cwd);
14
9
  let contentDir = ".";
@@ -27,6 +22,11 @@ export default async function createNewConfig() {
27
22
  const resolvedContentDir = nodePath.isAbsolute(contentDir)
28
23
  ? contentDir
29
24
  : nodePath.resolve(cwd, contentDir);
25
+ const configPath = nodePath.join(resolvedContentDir, "veslx.yaml");
26
+ if (fs.existsSync(configPath)) {
27
+ console.error(`Configuration file '${nodePath.relative(cwd, configPath) || "veslx.yaml"}' already exists.`);
28
+ return;
29
+ }
30
30
  if (!fs.existsSync(resolvedContentDir)) {
31
31
  await fs.promises.mkdir(resolvedContentDir, { recursive: true });
32
32
  console.log(`Created content directory: ${contentDir}`);
@@ -38,21 +38,34 @@ export default async function createNewConfig() {
38
38
  }
39
39
  else {
40
40
  const safeTitle = folderName.replace(/"/g, '\\"');
41
- const homepage = `---\ntitle: "${safeTitle}"\n---\n\n<VeslxFrontMatter />\n\n# ${folderName}\n\n<VeslxPostList />\n`;
41
+ const homepage = `---\ntitle: "Welcome to ${safeTitle}"\n---\n\n<VeslxFrontMatter />\n\n# Welcome to ${safeTitle}\n\nThis is your new veslx site. Start by editing posts or adding new folders.\n\n<VeslxPostList />\n`;
42
42
  await fs.promises.writeFile(indexPath, homepage, "utf-8");
43
43
  console.log(`Created custom homepage: ${nodePath.relative(cwd, indexPath) || "index.mdx"}`);
44
44
  }
45
45
  }
46
+ const sampleDir = nodePath.join(resolvedContentDir, "getting-started");
47
+ const samplePath = nodePath.join(sampleDir, "README.mdx");
48
+ if (fs.existsSync(samplePath)) {
49
+ console.log(`Skipped sample post: ${nodePath.relative(cwd, samplePath)}`);
50
+ }
51
+ else {
52
+ await fs.promises.mkdir(sampleDir, { recursive: true });
53
+ const safeTitle = folderName.replace(/"/g, '\\"');
54
+ const samplePost = `---\ntitle: "Getting Started"\ndate: "${new Date().toISOString().slice(0, 10)}"\ndescription: "Your first veslx post"\n---\n\n# Getting Started\n\nWelcome to **${safeTitle}**. This is a sample post you can edit or delete.\n\n- Write in MDX\n- Add images under \`./images\`\n- Create slides with \`SLIDES.mdx\`\n\n`;
55
+ await fs.promises.writeFile(samplePath, samplePost, "utf-8");
56
+ console.log(`Created sample post: ${nodePath.relative(cwd, samplePath)}`);
57
+ }
46
58
  const config = {
47
59
  dir: contentDir,
48
60
  site: {
49
- name: folderName,
61
+ name: "veslx",
62
+ description: "my new veslx site",
50
63
  github: "",
51
64
  },
52
65
  };
53
66
  const configStr = yaml.dump(config, { indent: 2, quotingType: '"' });
54
67
  await fs.promises.writeFile(configPath, configStr, "utf-8");
55
- console.log(`Created veslx.yaml`);
68
+ console.log(`Created ${nodePath.relative(cwd, configPath) || "veslx.yaml"}`);
56
69
  console.log(`\nEdit the file to customize your site, then run:`);
57
- console.log(` bunx veslx serve`);
70
+ console.log(` bunx veslx serve ${contentDir}`);
58
71
  }
@@ -79,15 +79,28 @@ function useGalleryImages({
79
79
  if (!directory) return [];
80
80
  let imagePaths;
81
81
  if (globs && globs.length > 0) {
82
- const allImages = collectAllImages(directory);
83
- imagePaths = allImages.map((img) => img.path).filter((p) => globs.some((glob) => minimatch(p, glob, { matchBase: true })));
82
+ const allImages = collectAllImages(directory).map((img) => img.path);
83
+ const seen = /* @__PURE__ */ new Set();
84
+ imagePaths = [];
85
+ for (const glob of globs) {
86
+ const matches = allImages.filter(
87
+ (p) => !seen.has(p) && minimatch(p, glob, { matchBase: true })
88
+ );
89
+ sortPathsNumerically(matches);
90
+ for (const match of matches) {
91
+ seen.add(match);
92
+ imagePaths.push(match);
93
+ }
94
+ }
84
95
  } else {
85
96
  const imageChildren = directory.children.filter((child) => {
86
97
  return !!child.name.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i) && child.type === "file";
87
98
  });
88
99
  imagePaths = imageChildren.map((child) => child.path);
89
100
  }
90
- sortPathsNumerically(imagePaths);
101
+ if (!globs || globs.length === 0) {
102
+ sortPathsNumerically(imagePaths);
103
+ }
91
104
  let filtered = filterPathsByTheme(imagePaths, resolvedTheme);
92
105
  if (limit) {
93
106
  filtered = filtered.slice(page * limit, (page + 1) * limit);
@@ -1 +1 @@
1
- {"version":3,"file":"use-gallery-images.js","sources":["../../../../../src/components/gallery/hooks/use-gallery-images.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useTheme } from \"next-themes\";\nimport { useParams } from \"react-router-dom\";\nimport { useDirectory } from \"../../../../plugin/src/client\";\nimport { FileEntry, DirectoryEntry } from \"../../../../plugin/src/lib\";\nimport { minimatch } from \"minimatch\";\n\n// Recursively collect all image files from a directory tree\nfunction collectAllImages(entry: DirectoryEntry | FileEntry): FileEntry[] {\n if (entry.type === \"file\") {\n if (entry.name.match(/\\.(png|jpg|jpeg|gif|svg|webp)$/i)) {\n return [entry];\n }\n return [];\n }\n // It's a directory - recurse into children\n const images: FileEntry[] = [];\n for (const child of entry.children || []) {\n images.push(...collectAllImages(child));\n }\n return images;\n}\n\nfunction sortPathsNumerically(paths: string[]): void {\n paths.sort((a, b) => {\n const nums = (s: string) => (s.match(/\\d+/g) || []).map(Number);\n const na = nums(a);\n const nb = nums(b);\n const len = Math.max(na.length, nb.length);\n for (let i = 0; i < len; i++) {\n const diff = (na[i] ?? 0) - (nb[i] ?? 0);\n if (diff !== 0) return diff;\n }\n return a.localeCompare(b);\n });\n}\n\nfunction filterPathsByTheme(paths: string[], theme: string | undefined): string[] {\n const pathGroups = new Map<string, { light?: string; dark?: string; original?: string }>();\n\n paths.forEach((path) => {\n const cleanPath = path.split(/[?#]/)[0];\n const themedMatch = cleanPath.match(/^(.*)_(light|dark)\\.(png|jpe?g|gif|svg|webp)$/i);\n if (themedMatch) {\n const baseName = `${themedMatch[1]}.${themedMatch[3]}`;\n const variant = themedMatch[2].toLowerCase() as \"light\" | \"dark\";\n const group = pathGroups.get(baseName) || {};\n group[variant] = path;\n pathGroups.set(baseName, group);\n } else {\n pathGroups.set(path, { original: path });\n }\n });\n\n const filtered: string[] = [];\n pathGroups.forEach((group, baseName) => {\n if (group.original) {\n filtered.push(group.original);\n } else {\n const isDark = theme === \"dark\";\n const preferredPath = isDark ? group.dark : group.light;\n const fallbackPath = isDark ? group.light : group.dark;\n filtered.push(preferredPath || fallbackPath || baseName);\n }\n });\n\n return filtered;\n}\n\n\nexport function useGalleryImages({\n path,\n globs = null,\n limit,\n page = 0,\n}: {\n path?: string;\n globs?: string[] | null;\n limit?: number | null;\n page?: number;\n}) {\n const { resolvedTheme } = useTheme();\n const { \"*\": routePath = \"\" } = useParams();\n\n // Get the current post's directory from the route\n // Route is like \"04-components/README.mdx\" -> \"04-components\"\n // Or \"14-gallery.mdx\" -> \".\" (root level file)\n const currentDir = routePath\n .replace(/\\/?[^/]+\\.mdx?$/i, \"\") // Remove [/]filename.mdx/.md (slash optional for root files)\n .replace(/\\/$/, \"\") // Remove trailing slash\n || \".\";\n\n // Resolve the path relative to current directory\n let resolvedPath = path;\n if (path?.startsWith(\"./\")) {\n // Relative path like \"./images\" -> \"gallery-examples/images\"\n const relativePart = path.slice(2);\n resolvedPath = currentDir === \".\" ? relativePart : `${currentDir}/${relativePart}`;\n } else if (path && !path.startsWith(\"/\") && !path.includes(\"/\")) {\n // Simple name like \"images\" -> \"gallery-examples/images\"\n resolvedPath = currentDir === \".\" ? path : `${currentDir}/${path}`;\n }\n\n // If only globs provided (no path), use root directory\n const directoryPath = resolvedPath || \".\";\n const { directory, error } = useDirectory(directoryPath);\n\n const paths = useMemo(() => {\n if (!directory) return [];\n\n let imagePaths: string[];\n\n if (globs && globs.length > 0) {\n // When globs provided, collect all images recursively and match against filename\n const allImages = collectAllImages(directory);\n imagePaths = allImages\n .map(img => img.path)\n .filter(p => globs.some(glob => minimatch(p, glob, { matchBase: true })));\n } else {\n // No globs - just get images from the specified directory\n const imageChildren = directory.children.filter((child): child is FileEntry => {\n return !!child.name.match(/\\.(png|jpg|jpeg|gif|svg|webp)$/i) && child.type === \"file\";\n });\n imagePaths = imageChildren.map(child => child.path);\n }\n\n sortPathsNumerically(imagePaths);\n let filtered = filterPathsByTheme(imagePaths, resolvedTheme);\n\n if (limit) {\n filtered = filtered.slice(page * limit, (page + 1) * limit);\n }\n\n\n return filtered;\n }, [directory, globs, resolvedTheme, limit, page]);\n\n return {\n paths,\n isLoading: !directory && !error,\n isEmpty: !!error || (directory !== null && paths.length === 0),\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-gallery-images.js","sources":["../../../../../src/components/gallery/hooks/use-gallery-images.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useTheme } from \"next-themes\";\nimport { useParams } from \"react-router-dom\";\nimport { useDirectory } from \"../../../../plugin/src/client\";\nimport { FileEntry, DirectoryEntry } from \"../../../../plugin/src/lib\";\nimport { minimatch } from \"minimatch\";\n\n// Recursively collect all image files from a directory tree\nfunction collectAllImages(entry: DirectoryEntry | FileEntry): FileEntry[] {\n if (entry.type === \"file\") {\n if (entry.name.match(/\\.(png|jpg|jpeg|gif|svg|webp)$/i)) {\n return [entry];\n }\n return [];\n }\n // It's a directory - recurse into children\n const images: FileEntry[] = [];\n for (const child of entry.children || []) {\n images.push(...collectAllImages(child));\n }\n return images;\n}\n\nfunction sortPathsNumerically(paths: string[]): void {\n paths.sort((a, b) => {\n const nums = (s: string) => (s.match(/\\d+/g) || []).map(Number);\n const na = nums(a);\n const nb = nums(b);\n const len = Math.max(na.length, nb.length);\n for (let i = 0; i < len; i++) {\n const diff = (na[i] ?? 0) - (nb[i] ?? 0);\n if (diff !== 0) return diff;\n }\n return a.localeCompare(b);\n });\n}\n\nfunction filterPathsByTheme(paths: string[], theme: string | undefined): string[] {\n const pathGroups = new Map<string, { light?: string; dark?: string; original?: string }>();\n\n paths.forEach((path) => {\n const cleanPath = path.split(/[?#]/)[0];\n const themedMatch = cleanPath.match(/^(.*)_(light|dark)\\.(png|jpe?g|gif|svg|webp)$/i);\n if (themedMatch) {\n const baseName = `${themedMatch[1]}.${themedMatch[3]}`;\n const variant = themedMatch[2].toLowerCase() as \"light\" | \"dark\";\n const group = pathGroups.get(baseName) || {};\n group[variant] = path;\n pathGroups.set(baseName, group);\n } else {\n pathGroups.set(path, { original: path });\n }\n });\n\n const filtered: string[] = [];\n pathGroups.forEach((group, baseName) => {\n if (group.original) {\n filtered.push(group.original);\n } else {\n const isDark = theme === \"dark\";\n const preferredPath = isDark ? group.dark : group.light;\n const fallbackPath = isDark ? group.light : group.dark;\n filtered.push(preferredPath || fallbackPath || baseName);\n }\n });\n\n return filtered;\n}\n\n\nexport function useGalleryImages({\n path,\n globs = null,\n limit,\n page = 0,\n}: {\n path?: string;\n globs?: string[] | null;\n limit?: number | null;\n page?: number;\n}) {\n const { resolvedTheme } = useTheme();\n const { \"*\": routePath = \"\" } = useParams();\n\n // Get the current post's directory from the route\n // Route is like \"04-components/README.mdx\" -> \"04-components\"\n // Or \"14-gallery.mdx\" -> \".\" (root level file)\n const currentDir = routePath\n .replace(/\\/?[^/]+\\.mdx?$/i, \"\") // Remove [/]filename.mdx/.md (slash optional for root files)\n .replace(/\\/$/, \"\") // Remove trailing slash\n || \".\";\n\n // Resolve the path relative to current directory\n let resolvedPath = path;\n if (path?.startsWith(\"./\")) {\n // Relative path like \"./images\" -> \"gallery-examples/images\"\n const relativePart = path.slice(2);\n resolvedPath = currentDir === \".\" ? relativePart : `${currentDir}/${relativePart}`;\n } else if (path && !path.startsWith(\"/\") && !path.includes(\"/\")) {\n // Simple name like \"images\" -> \"gallery-examples/images\"\n resolvedPath = currentDir === \".\" ? path : `${currentDir}/${path}`;\n }\n\n // If only globs provided (no path), use root directory\n const directoryPath = resolvedPath || \".\";\n const { directory, error } = useDirectory(directoryPath);\n\n const paths = useMemo(() => {\n if (!directory) return [];\n\n let imagePaths: string[];\n\n if (globs && globs.length > 0) {\n // When globs provided, preserve glob ordering and avoid duplicates.\n const allImages = collectAllImages(directory).map((img) => img.path);\n const seen = new Set<string>();\n imagePaths = [];\n\n for (const glob of globs) {\n const matches = allImages.filter((p) =>\n !seen.has(p) && minimatch(p, glob, { matchBase: true })\n );\n sortPathsNumerically(matches);\n for (const match of matches) {\n seen.add(match);\n imagePaths.push(match);\n }\n }\n } else {\n // No globs - just get images from the specified directory\n const imageChildren = directory.children.filter((child): child is FileEntry => {\n return !!child.name.match(/\\.(png|jpg|jpeg|gif|svg|webp)$/i) && child.type === \"file\";\n });\n imagePaths = imageChildren.map(child => child.path);\n }\n\n if (!globs || globs.length === 0) {\n sortPathsNumerically(imagePaths);\n }\n let filtered = filterPathsByTheme(imagePaths, resolvedTheme);\n\n if (limit) {\n filtered = filtered.slice(page * limit, (page + 1) * limit);\n }\n\n\n return filtered;\n }, [directory, globs, resolvedTheme, limit, page]);\n\n return {\n paths,\n isLoading: !directory && !error,\n isEmpty: !!error || (directory !== null && paths.length === 0),\n };\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veslx",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -111,11 +111,21 @@ export function useGalleryImages({
111
111
  let imagePaths: string[];
112
112
 
113
113
  if (globs && globs.length > 0) {
114
- // When globs provided, collect all images recursively and match against filename
115
- const allImages = collectAllImages(directory);
116
- imagePaths = allImages
117
- .map(img => img.path)
118
- .filter(p => globs.some(glob => minimatch(p, glob, { matchBase: true })));
114
+ // When globs provided, preserve glob ordering and avoid duplicates.
115
+ const allImages = collectAllImages(directory).map((img) => img.path);
116
+ const seen = new Set<string>();
117
+ imagePaths = [];
118
+
119
+ for (const glob of globs) {
120
+ const matches = allImages.filter((p) =>
121
+ !seen.has(p) && minimatch(p, glob, { matchBase: true })
122
+ );
123
+ sortPathsNumerically(matches);
124
+ for (const match of matches) {
125
+ seen.add(match);
126
+ imagePaths.push(match);
127
+ }
128
+ }
119
129
  } else {
120
130
  // No globs - just get images from the specified directory
121
131
  const imageChildren = directory.children.filter((child): child is FileEntry => {
@@ -124,7 +134,9 @@ export function useGalleryImages({
124
134
  imagePaths = imageChildren.map(child => child.path);
125
135
  }
126
136
 
127
- sortPathsNumerically(imagePaths);
137
+ if (!globs || globs.length === 0) {
138
+ sortPathsNumerically(imagePaths);
139
+ }
128
140
  let filtered = filterPathsByTheme(imagePaths, resolvedTheme);
129
141
 
130
142
  if (limit) {