veslx 0.1.34 → 0.1.35

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/start.ts CHANGED
@@ -18,6 +18,7 @@ export default async function start(dir?: string) {
18
18
  pm2.connect((err) => {
19
19
  if (err) {
20
20
  log.error('pm2 connection failed');
21
+ process.exit(1);
21
22
  return;
22
23
  }
23
24
 
@@ -34,10 +35,12 @@ export default async function start(dir?: string) {
34
35
 
35
36
  if (err) {
36
37
  log.error('daemon failed to start');
38
+ process.exit(1);
37
39
  return;
38
40
  }
39
41
 
40
42
  log.success('daemon started');
43
+ process.exit(0);
41
44
  });
42
45
  })
43
46
  }
@@ -67,6 +67,7 @@ function Gallery({
67
67
  const imageElement = (index, img, className) => /* @__PURE__ */ jsx(
68
68
  "div",
69
69
  {
70
+ title: img.label,
70
71
  className: `aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group ${className || ""}`,
71
72
  onClick: () => lightbox.open(index),
72
73
  children: /* @__PURE__ */ jsx(
@@ -97,9 +98,10 @@ function Gallery({
97
98
  /* @__PURE__ */ jsx(FigureHeader, { title, subtitle }),
98
99
  imageElement(0, images[0]),
99
100
  /* @__PURE__ */ jsx(FigureCaption, { caption, label: captionLabel })
100
- ] }) : isCompact ? /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: images.map((img, index) => imageElement(index, img, "flex-1")) }) : /* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-4", children: images.map((img, index) => /* @__PURE__ */ jsx(
101
+ ] }) : isCompact ? /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: images.map((img, index) => imageElement(index, img, "flex-1")) }) : /* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto overscroll-x-contain pb-4", children: images.map((img, index) => /* @__PURE__ */ jsx(
101
102
  "div",
102
103
  {
104
+ title: img.label,
103
105
  className: "flex-none w-[30%] min-w-[250px] aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group",
104
106
  onClick: () => lightbox.open(index),
105
107
  children: /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/components/gallery/index.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { Image } from \"lucide-react\";\nimport { Lightbox, LightboxImage } from \"@/components/gallery/components/lightbox\";\nimport { useGalleryImages } from \"./hooks/use-gallery-images\";\nimport { useLightbox } from \"./hooks/use-lightbox\";\nimport { LoadingImage } from \"./components/loading-image\";\nimport { FigureHeader } from \"./components/figure-header\";\nimport { FigureCaption } from \"./components/figure-caption\";\n\nfunction getImageLabel(path: string): string {\n const filename = path.split('/').pop() || path;\n return filename\n .replace(/\\.(png|jpg|jpeg|gif|svg|webp)$/i, '')\n .replace(/[_-]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction getImageUrl(path: string): string {\n return `/raw/${path}`;\n}\n\nexport default function Gallery({\n path,\n globs = null,\n caption,\n captionLabel,\n title,\n subtitle,\n limit = null,\n page = 0,\n children,\n childAlign = \"right\",\n}: {\n path?: string;\n globs?: string[] | null;\n caption?: string;\n captionLabel?: string;\n title?: string;\n subtitle?: string;\n limit?: number | null;\n page?: number;\n children?: React.ReactNode;\n childAlign?: \"left\" | \"right\";\n}) {\n const { paths, isLoading, isEmpty } = useGalleryImages({\n path,\n globs,\n limit,\n page: page,\n });\n\n const lightbox = useLightbox(paths.length);\n\n const images: LightboxImage[] = useMemo(() =>\n paths.map(p => ({ src: getImageUrl(p), label: getImageLabel(p) })),\n [paths]\n );\n\n if (isLoading) {\n return (\n <figure className=\"not-prose py-6 md:py-8\">\n <div className=\"grid grid-cols-3 gap-3 max-w-[var(--gallery-width)] mx-auto\">\n {[...Array(3)].map((_, i) => (\n <div\n key={i}\n className=\"aspect-square rounded-sm bg-muted/20 relative overflow-hidden\"\n >\n <div\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-muted/30 to-transparent animate-shimmer\"\n style={{ animationDelay: `${i * 150}ms` }}\n />\n </div>\n ))}\n </div>\n </figure>\n );\n }\n\n if (isEmpty) {\n return (\n <figure className=\"not-prose py-12 text-center\">\n <div className=\"inline-flex items-center gap-2.5 text-muted-foreground/40\">\n <Image className=\"h-3.5 w-3.5\" strokeWidth={1.5} />\n <span className=\"font-mono text-xs uppercase tracking-widest\">No images</span>\n </div>\n </figure>\n );\n }\n\n const isSingle = images.length === 1;\n const isTwo = images.length === 2;\n const isCompact = images.length <= 3;\n const isSingleWithChildren = images.length === 1 && children;\n // 2-3 images should break out of the content width\n const shouldBreakOut = images.length >= 2;\n\n const imageElement = (index: number, img: LightboxImage, className?: string) => (\n <div\n key={index}\n className={`aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group ${className || ''}`}\n onClick={() => lightbox.open(index)}\n >\n <LoadingImage\n src={img.src}\n alt={img.label}\n className=\"object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]\"\n />\n </div>\n );\n\n return (\n <>\n <figure className={`not-prose relative py-6 md:py-8 ${shouldBreakOut ? (isTwo ? 'w-[75vw] ml-[calc(-37.5vw+50%)]' : isCompact ? 'w-[96vw] ml-[calc(-48vw+50%)]' : 'w-[var(--gallery-width)] ml-[calc(-45vw+50%)]') : ''}`}>\n {!isSingleWithChildren && !isSingle && (\n <div className=\"max-w-[var(--content-width)] mx-auto px-[var(--page-padding)]\">\n <FigureHeader title={title} subtitle={subtitle} />\n </div>\n )}\n\n {isSingleWithChildren ? (\n <div className={`flex gap-6 ${childAlign === 'left' ? '' : 'flex-row-reverse'}`}>\n <div className=\"flex-1 text-sm leading-relaxed text-foreground/90 space-y-3 [&>ul]:space-y-1.5 [&>ul]:list-disc [&>ul]:pl-5 [&>ol]:space-y-1.5 [&>ol]:list-decimal [&>ol]:pl-5 flex flex-col\">\n {(title || subtitle) && <div className=\"invisible\"><FigureHeader title={title} subtitle={subtitle} /></div>}\n <div>{children}</div>\n </div>\n <div className=\"w-3/5 flex-shrink-0\">\n <FigureHeader title={title} subtitle={subtitle} />\n {imageElement(0, images[0])}\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n </div>\n ) : isSingle ? (\n <div className=\"max-w-[70%] mx-auto\">\n <FigureHeader title={title} subtitle={subtitle} />\n {imageElement(0, images[0])}\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n ) : isCompact ? (\n <div className=\"flex gap-3\">\n {images.map((img, index) => imageElement(index, img, 'flex-1'))}\n </div>\n ) : (\n <div className=\"flex gap-3 overflow-x-auto pb-4\">\n {images.map((img, index) => (\n <div\n key={index}\n className=\"flex-none w-[30%] min-w-[250px] aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group\"\n onClick={() => lightbox.open(index)}\n >\n <LoadingImage\n src={img.src}\n alt={img.label}\n className=\"object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]\"\n />\n </div>\n ))}\n </div>\n )}\n\n {!isSingleWithChildren && !isSingle && (\n <div className=\"max-w-[var(--content-width)] mx-auto px-[var(--page-padding)]\">\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n )}\n </figure>\n\n {lightbox.isOpen && lightbox.selectedIndex !== null && (\n <Lightbox\n images={images}\n selectedIndex={lightbox.selectedIndex}\n onClose={lightbox.close}\n onPrevious={lightbox.goToPrevious}\n onNext={lightbox.goToNext}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AASA,SAAS,cAAc,MAAsB;AAC3C,QAAM,WAAW,KAAK,MAAM,GAAG,EAAE,SAAS;AAC1C,SAAO,SACJ,QAAQ,mCAAmC,EAAE,EAC7C,QAAQ,SAAS,GAAG,EACpB,QAAQ,QAAQ,GAAG,EACnB,KAAA;AACL;AAEA,SAAS,YAAY,MAAsB;AACzC,SAAO,QAAQ,IAAI;AACrB;AAEA,SAAwB,QAAQ;AAAA,EAC9B;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,OAAO;AAAA,EACP;AAAA,EACA,aAAa;AACf,GAWG;AACD,QAAM,EAAE,OAAO,WAAW,QAAA,IAAY,iBAAiB;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,WAAW,YAAY,MAAM,MAAM;AAEzC,QAAM,SAA0B;AAAA,IAAQ,MACtC,MAAM,IAAI,CAAA,OAAM,EAAE,KAAK,YAAY,CAAC,GAAG,OAAO,cAAc,CAAC,IAAI;AAAA,IACjE,CAAC,KAAK;AAAA,EAAA;AAGR,MAAI,WAAW;AACb,+BACG,UAAA,EAAO,WAAU,0BAChB,UAAA,oBAAC,SAAI,WAAU,+DACZ,UAAA,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,MACrB;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,WAAU;AAAA,QAEV,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,KAAA;AAAA,UAAK;AAAA,QAAA;AAAA,MAC1C;AAAA,MANK;AAAA,IAAA,CAQR,GACH,EAAA,CACF;AAAA,EAEJ;AAEA,MAAI,SAAS;AACX,+BACG,UAAA,EAAO,WAAU,+BAChB,UAAA,qBAAC,OAAA,EAAI,WAAU,6DACb,UAAA;AAAA,MAAA,oBAAC,OAAA,EAAM,WAAU,eAAc,aAAa,KAAK;AAAA,MACjD,oBAAC,QAAA,EAAK,WAAU,+CAA8C,UAAA,YAAA,CAAS;AAAA,IAAA,EAAA,CACzE,EAAA,CACF;AAAA,EAEJ;AAEA,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,QAAQ,OAAO,WAAW;AAChC,QAAM,YAAY,OAAO,UAAU;AACnC,QAAM,uBAAuB,OAAO,WAAW,KAAK;AAEpD,QAAM,iBAAiB,OAAO,UAAU;AAExC,QAAM,eAAe,CAAC,OAAe,KAAoB,cACvD;AAAA,IAAC;AAAA,IAAA;AAAA,MAEC,WAAW,6EAA6E,aAAa,EAAE;AAAA,MACvG,SAAS,MAAM,SAAS,KAAK,KAAK;AAAA,MAElC,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,KAAK,IAAI;AAAA,UACT,KAAK,IAAI;AAAA,UACT,WAAU;AAAA,QAAA;AAAA,MAAA;AAAA,IACZ;AAAA,IARK;AAAA,EAAA;AAYT,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,qBAAC,UAAA,EAAO,WAAW,mCAAmC,iBAAkB,QAAQ,oCAAoC,YAAY,kCAAkC,kDAAmD,EAAE,IACpN,UAAA;AAAA,MAAA,CAAC,wBAAwB,CAAC,YACzB,oBAAC,OAAA,EAAI,WAAU,iEACb,UAAA,oBAAC,cAAA,EAAa,OAAc,SAAA,CAAoB,EAAA,CAClD;AAAA,MAGD,4CACE,OAAA,EAAI,WAAW,cAAc,eAAe,SAAS,KAAK,kBAAkB,IAC3E,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,gLACX,UAAA;AAAA,WAAA,SAAS,iCAAc,OAAA,EAAI,WAAU,aAAY,UAAA,oBAAC,cAAA,EAAa,OAAc,SAAA,CAAoB,EAAA,CAAE;AAAA,UACrG,oBAAC,SAAK,SAAA,CAAS;AAAA,QAAA,GACjB;AAAA,QACA,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,UAAA,oBAAC,cAAA,EAAa,OAAc,SAAA,CAAoB;AAAA,UAC/C,aAAa,GAAG,OAAO,CAAC,CAAC;AAAA,UAC1B,oBAAC,eAAA,EAAc,SAAkB,OAAO,aAAA,CAAc;AAAA,QAAA,EAAA,CACxD;AAAA,MAAA,EAAA,CACF,IACE,WACF,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,QAAA,oBAAC,cAAA,EAAa,OAAc,SAAA,CAAoB;AAAA,QAC/C,aAAa,GAAG,OAAO,CAAC,CAAC;AAAA,QAC1B,oBAAC,eAAA,EAAc,SAAkB,OAAO,aAAA,CAAc;AAAA,MAAA,EAAA,CACxD,IACE,YACF,oBAAC,OAAA,EAAI,WAAU,cACZ,UAAA,OAAO,IAAI,CAAC,KAAK,UAAU,aAAa,OAAO,KAAK,QAAQ,CAAC,EAAA,CAChE,IAEA,oBAAC,OAAA,EAAI,WAAU,mCACZ,UAAA,OAAO,IAAI,CAAC,KAAK,UAChB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,WAAU;AAAA,UACV,SAAS,MAAM,SAAS,KAAK,KAAK;AAAA,UAElC,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,KAAK,IAAI;AAAA,cACT,KAAK,IAAI;AAAA,cACT,WAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACZ;AAAA,QARK;AAAA,MAAA,CAUR,GACH;AAAA,MAGD,CAAC,wBAAwB,CAAC,YACzB,oBAAC,OAAA,EAAI,WAAU,iEACb,UAAA,oBAAC,eAAA,EAAc,SAAkB,OAAO,cAAc,EAAA,CACxD;AAAA,IAAA,GAEJ;AAAA,IAEC,SAAS,UAAU,SAAS,kBAAkB,QAC7C;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAe,SAAS;AAAA,QACxB,SAAS,SAAS;AAAA,QAClB,YAAY,SAAS;AAAA,QACrB,QAAQ,SAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EACnB,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/gallery/index.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { Image } from \"lucide-react\";\nimport { Lightbox, LightboxImage } from \"@/components/gallery/components/lightbox\";\nimport { useGalleryImages } from \"./hooks/use-gallery-images\";\nimport { useLightbox } from \"./hooks/use-lightbox\";\nimport { LoadingImage } from \"./components/loading-image\";\nimport { FigureHeader } from \"./components/figure-header\";\nimport { FigureCaption } from \"./components/figure-caption\";\n\nfunction getImageLabel(path: string): string {\n const filename = path.split('/').pop() || path;\n return filename\n .replace(/\\.(png|jpg|jpeg|gif|svg|webp)$/i, '')\n .replace(/[_-]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction getImageUrl(path: string): string {\n return `/raw/${path}`;\n}\n\nexport default function Gallery({\n path,\n globs = null,\n caption,\n captionLabel,\n title,\n subtitle,\n limit = null,\n page = 0,\n children,\n childAlign = \"right\",\n}: {\n path?: string;\n globs?: string[] | null;\n caption?: string;\n captionLabel?: string;\n title?: string;\n subtitle?: string;\n limit?: number | null;\n page?: number;\n children?: React.ReactNode;\n childAlign?: \"left\" | \"right\";\n}) {\n const { paths, isLoading, isEmpty } = useGalleryImages({\n path,\n globs,\n limit,\n page: page,\n });\n\n const lightbox = useLightbox(paths.length);\n\n const images: LightboxImage[] = useMemo(() =>\n paths.map(p => ({ src: getImageUrl(p), label: getImageLabel(p) })),\n [paths]\n );\n\n if (isLoading) {\n return (\n <figure className=\"not-prose py-6 md:py-8\">\n <div className=\"grid grid-cols-3 gap-3 max-w-[var(--gallery-width)] mx-auto\">\n {[...Array(3)].map((_, i) => (\n <div\n key={i}\n className=\"aspect-square rounded-sm bg-muted/20 relative overflow-hidden\"\n >\n <div\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-muted/30 to-transparent animate-shimmer\"\n style={{ animationDelay: `${i * 150}ms` }}\n />\n </div>\n ))}\n </div>\n </figure>\n );\n }\n\n if (isEmpty) {\n return (\n <figure className=\"not-prose py-12 text-center\">\n <div className=\"inline-flex items-center gap-2.5 text-muted-foreground/40\">\n <Image className=\"h-3.5 w-3.5\" strokeWidth={1.5} />\n <span className=\"font-mono text-xs uppercase tracking-widest\">No images</span>\n </div>\n </figure>\n );\n }\n\n const isSingle = images.length === 1;\n const isTwo = images.length === 2;\n const isCompact = images.length <= 3;\n const isSingleWithChildren = images.length === 1 && children;\n // 2-3 images should break out of the content width\n const shouldBreakOut = images.length >= 2;\n\n const imageElement = (index: number, img: LightboxImage, className?: string) => (\n <div\n key={index}\n title={img.label}\n className={`aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group ${className || ''}`}\n onClick={() => lightbox.open(index)}\n >\n <LoadingImage\n src={img.src}\n alt={img.label}\n className=\"object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]\"\n />\n </div>\n );\n\n return (\n <>\n <figure className={`not-prose relative py-6 md:py-8 ${shouldBreakOut ? (isTwo ? 'w-[75vw] ml-[calc(-37.5vw+50%)]' : isCompact ? 'w-[96vw] ml-[calc(-48vw+50%)]' : 'w-[var(--gallery-width)] ml-[calc(-45vw+50%)]') : ''}`}>\n {!isSingleWithChildren && !isSingle && (\n <div className=\"max-w-[var(--content-width)] mx-auto px-[var(--page-padding)]\">\n <FigureHeader title={title} subtitle={subtitle} />\n </div>\n )}\n\n {isSingleWithChildren ? (\n <div className={`flex gap-6 ${childAlign === 'left' ? '' : 'flex-row-reverse'}`}>\n <div className=\"flex-1 text-sm leading-relaxed text-foreground/90 space-y-3 [&>ul]:space-y-1.5 [&>ul]:list-disc [&>ul]:pl-5 [&>ol]:space-y-1.5 [&>ol]:list-decimal [&>ol]:pl-5 flex flex-col\">\n {(title || subtitle) && <div className=\"invisible\"><FigureHeader title={title} subtitle={subtitle} /></div>}\n <div>{children}</div>\n </div>\n <div className=\"w-3/5 flex-shrink-0\">\n <FigureHeader title={title} subtitle={subtitle} />\n {imageElement(0, images[0])}\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n </div>\n ) : isSingle ? (\n <div className=\"max-w-[70%] mx-auto\">\n <FigureHeader title={title} subtitle={subtitle} />\n {imageElement(0, images[0])}\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n ) : isCompact ? (\n <div className=\"flex gap-3\">\n {images.map((img, index) => imageElement(index, img, 'flex-1'))}\n </div>\n ) : (\n <div className=\"flex gap-3 overflow-x-auto overscroll-x-contain pb-4\">\n {images.map((img, index) => (\n <div\n key={index}\n title={img.label}\n className=\"flex-none w-[30%] min-w-[250px] aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group\"\n onClick={() => lightbox.open(index)}\n >\n <LoadingImage\n src={img.src}\n alt={img.label}\n className=\"object-contain transition-transform duration-500 ease-out group-hover:scale-[1.02]\"\n />\n </div>\n ))}\n </div>\n )}\n\n {!isSingleWithChildren && !isSingle && (\n <div className=\"max-w-[var(--content-width)] mx-auto px-[var(--page-padding)]\">\n <FigureCaption caption={caption} label={captionLabel} />\n </div>\n )}\n </figure>\n\n {lightbox.isOpen && lightbox.selectedIndex !== null && (\n <Lightbox\n images={images}\n selectedIndex={lightbox.selectedIndex}\n onClose={lightbox.close}\n onPrevious={lightbox.goToPrevious}\n onNext={lightbox.goToNext}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -65,9 +65,7 @@ function formatNumber(value) {
65
65
  if (Math.abs(value) < 1e-4 && value !== 0) return value.toExponential(1);
66
66
  if (Math.abs(value) >= 1e4) return value.toExponential(1);
67
67
  if (Number.isInteger(value)) return value.toString();
68
- const str = value.toString();
69
- if (str.length > 6) return value.toPrecision(4);
70
- return str;
68
+ return value.toFixed(1);
71
69
  }
72
70
  function formatValue(value) {
73
71
  if (value === null) return "null";
@@ -1 +1 @@
1
- {"version":3,"file":"parameter-utils.js","sources":["../../../src/lib/parameter-utils.ts"],"sourcesContent":["import { load } from \"js-yaml\";\n\nexport type ParameterValue = string | number | boolean | null | ParameterValue[] | { [key: string]: ParameterValue };\n\n/**\n * Extract a value from nested data using a jq-like path.\n * Supports:\n * - .foo.bar → nested keys\n * - .foo[0] → array index\n * - .foo[] → all array elements (returns array)\n */\nexport function extractPath(data: ParameterValue, path: string): ParameterValue | undefined {\n if (!path || path === \".\") return data;\n\n const cleanPath = path.startsWith(\".\") ? path.slice(1) : path;\n if (!cleanPath) return data;\n\n const segments: Array<{ type: \"key\" | \"index\" | \"all\"; value: string | number }> = [];\n let current = \"\";\n let i = 0;\n\n while (i < cleanPath.length) {\n const char = cleanPath[i];\n\n if (char === \".\") {\n if (current) {\n segments.push({ type: \"key\", value: current });\n current = \"\";\n }\n i++;\n } else if (char === \"[\") {\n if (current) {\n segments.push({ type: \"key\", value: current });\n current = \"\";\n }\n const closeIdx = cleanPath.indexOf(\"]\", i);\n if (closeIdx === -1) return undefined;\n const inner = cleanPath.slice(i + 1, closeIdx);\n if (inner === \"\") {\n segments.push({ type: \"all\", value: 0 });\n } else {\n const idx = parseInt(inner, 10);\n if (isNaN(idx)) return undefined;\n segments.push({ type: \"index\", value: idx });\n }\n i = closeIdx + 1;\n } else {\n current += char;\n i++;\n }\n }\n if (current) {\n segments.push({ type: \"key\", value: current });\n }\n\n let result: ParameterValue | undefined = data;\n\n for (const seg of segments) {\n if (result === null || result === undefined) return undefined;\n\n if (seg.type === \"key\") {\n if (typeof result !== \"object\" || Array.isArray(result)) return undefined;\n result = (result as Record<string, ParameterValue>)[seg.value as string];\n } else if (seg.type === \"index\") {\n if (!Array.isArray(result)) return undefined;\n result = result[seg.value as number];\n } else if (seg.type === \"all\") {\n if (!Array.isArray(result)) return undefined;\n // Return the array itself for further processing\n }\n }\n\n return result;\n}\n\nexport function getValueType(value: ParameterValue): \"string\" | \"number\" | \"boolean\" | \"null\" | \"array\" | \"object\" {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"object\") return \"object\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n return \"string\";\n}\n\nexport function formatNumber(value: number): string {\n if (Math.abs(value) < 0.0001 && value !== 0) return value.toExponential(1);\n if (Math.abs(value) >= 10000) return value.toExponential(1);\n if (Number.isInteger(value)) return value.toString();\n const str = value.toString();\n if (str.length > 6) return value.toPrecision(4);\n return str;\n}\n\nexport function formatValue(value: ParameterValue): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") return formatNumber(value);\n if (Array.isArray(value)) return `[${value.length}]`;\n if (typeof value === \"object\") return \"{...}\";\n return String(value);\n}\n\n/**\n * Parse YAML or JSON content based on file extension.\n */\nexport function parseConfigFile(content: string, path: string): Record<string, ParameterValue> | null {\n if (path.endsWith(\".yaml\") || path.endsWith(\".yml\")) {\n try {\n return load(content) as Record<string, ParameterValue>;\n } catch {\n return null;\n }\n }\n\n if (path.endsWith(\".json\")) {\n try {\n return JSON.parse(content) as Record<string, ParameterValue>;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Derive a label from a jq-like keyPath.\n * E.g., \".base.N_E\" → \"N_E\"\n */\nexport function deriveLabelFromPath(keyPath: string): string {\n const cleanPath = keyPath.startsWith(\".\") ? keyPath.slice(1) : keyPath;\n const parts = cleanPath.split(\".\");\n return parts[parts.length - 1].replace(/\\[\\d+\\]/g, \"\");\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parameter-utils.js","sources":["../../../src/lib/parameter-utils.ts"],"sourcesContent":["import { load } from \"js-yaml\";\n\nexport type ParameterValue = string | number | boolean | null | ParameterValue[] | { [key: string]: ParameterValue };\n\n/**\n * Extract a value from nested data using a jq-like path.\n * Supports:\n * - .foo.bar → nested keys\n * - .foo[0] → array index\n * - .foo[] → all array elements (returns array)\n */\nexport function extractPath(data: ParameterValue, path: string): ParameterValue | undefined {\n if (!path || path === \".\") return data;\n\n const cleanPath = path.startsWith(\".\") ? path.slice(1) : path;\n if (!cleanPath) return data;\n\n const segments: Array<{ type: \"key\" | \"index\" | \"all\"; value: string | number }> = [];\n let current = \"\";\n let i = 0;\n\n while (i < cleanPath.length) {\n const char = cleanPath[i];\n\n if (char === \".\") {\n if (current) {\n segments.push({ type: \"key\", value: current });\n current = \"\";\n }\n i++;\n } else if (char === \"[\") {\n if (current) {\n segments.push({ type: \"key\", value: current });\n current = \"\";\n }\n const closeIdx = cleanPath.indexOf(\"]\", i);\n if (closeIdx === -1) return undefined;\n const inner = cleanPath.slice(i + 1, closeIdx);\n if (inner === \"\") {\n segments.push({ type: \"all\", value: 0 });\n } else {\n const idx = parseInt(inner, 10);\n if (isNaN(idx)) return undefined;\n segments.push({ type: \"index\", value: idx });\n }\n i = closeIdx + 1;\n } else {\n current += char;\n i++;\n }\n }\n if (current) {\n segments.push({ type: \"key\", value: current });\n }\n\n let result: ParameterValue | undefined = data;\n\n for (const seg of segments) {\n if (result === null || result === undefined) return undefined;\n\n if (seg.type === \"key\") {\n if (typeof result !== \"object\" || Array.isArray(result)) return undefined;\n result = (result as Record<string, ParameterValue>)[seg.value as string];\n } else if (seg.type === \"index\") {\n if (!Array.isArray(result)) return undefined;\n result = result[seg.value as number];\n } else if (seg.type === \"all\") {\n if (!Array.isArray(result)) return undefined;\n // Return the array itself for further processing\n }\n }\n\n return result;\n}\n\nexport function getValueType(value: ParameterValue): \"string\" | \"number\" | \"boolean\" | \"null\" | \"array\" | \"object\" {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n if (typeof value === \"object\") return \"object\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n return \"string\";\n}\n\nexport function formatNumber(value: number): string {\n if (Math.abs(value) < 0.0001 && value !== 0) return value.toExponential(1);\n if (Math.abs(value) >= 10000) return value.toExponential(1);\n if (Number.isInteger(value)) return value.toString();\n return value.toFixed(1);\n}\n\nexport function formatValue(value: ParameterValue): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") return formatNumber(value);\n if (Array.isArray(value)) return `[${value.length}]`;\n if (typeof value === \"object\") return \"{...}\";\n return String(value);\n}\n\n/**\n * Parse YAML or JSON content based on file extension.\n */\nexport function parseConfigFile(content: string, path: string): Record<string, ParameterValue> | null {\n if (path.endsWith(\".yaml\") || path.endsWith(\".yml\")) {\n try {\n return load(content) as Record<string, ParameterValue>;\n } catch {\n return null;\n }\n }\n\n if (path.endsWith(\".json\")) {\n try {\n return JSON.parse(content) as Record<string, ParameterValue>;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Derive a label from a jq-like keyPath.\n * E.g., \".base.N_E\" → \"N_E\"\n */\nexport function deriveLabelFromPath(keyPath: string): string {\n const cleanPath = keyPath.startsWith(\".\") ? keyPath.slice(1) : keyPath;\n const parts = cleanPath.split(\".\");\n return parts[parts.length - 1].replace(/\\[\\d+\\]/g, \"\");\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.34",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -431,6 +431,36 @@ export const modules = import.meta.glob(['@content/**/*.mdx', '@content/**/*.md'
431
431
  if (configPath && fs.existsSync(configPath)) {
432
432
  server.watcher.add(configPath)
433
433
  }
434
+
435
+ // Watch content directory for all file changes (add, delete, change)
436
+ server.watcher.add(dir)
437
+
438
+ // File extensions that should trigger a full reload
439
+ const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css']
440
+
441
+ const handleContentChange = (filePath: string, event: 'add' | 'unlink' | 'change') => {
442
+ // Check if the file is in the content directory
443
+ if (!filePath.startsWith(dir)) return
444
+
445
+ // Check if it's a watched file type
446
+ const ext = path.extname(filePath).toLowerCase()
447
+ if (!watchedExtensions.includes(ext)) return
448
+
449
+ console.log(`[veslx] Content ${event}: ${path.relative(dir, filePath)}`)
450
+
451
+ // Invalidate the virtual content module so frontmatters are re-extracted
452
+ const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_MODULE_ID)
453
+ if (mod) {
454
+ server.moduleGraph.invalidateModule(mod)
455
+ }
456
+
457
+ // Full reload to pick up new/deleted files
458
+ server.ws.send({ type: 'full-reload' })
459
+ }
460
+
461
+ server.watcher.on('add', (filePath) => handleContentChange(filePath, 'add'))
462
+ server.watcher.on('unlink', (filePath) => handleContentChange(filePath, 'unlink'))
463
+ server.watcher.on('change', (filePath) => handleContentChange(filePath, 'change'))
434
464
  },
435
465
 
436
466
  handleHotUpdate({ file, server }) {
@@ -448,6 +478,16 @@ export const modules = import.meta.glob(['@content/**/*.mdx', '@content/**/*.md'
448
478
  return [] // Prevent default HMR handling
449
479
  }
450
480
  }
481
+
482
+ // Check if the changed file is in the content directory
483
+ // Return empty array to prevent default HMR - we handle it in configureServer
484
+ if (file.startsWith(dir)) {
485
+ const watchedExtensions = ['.mdx', '.md', '.yaml', '.yml', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.tsx', '.ts', '.jsx', '.js', '.css']
486
+ const ext = path.extname(file).toLowerCase()
487
+ if (watchedExtensions.includes(ext)) {
488
+ return [] // Prevent default HMR, we already handle this via watcher events
489
+ }
490
+ }
451
491
  },
452
492
  configurePreviewServer(server) {
453
493
  // Add middleware for preview server too
@@ -98,6 +98,7 @@ export default function Gallery({
98
98
  const imageElement = (index: number, img: LightboxImage, className?: string) => (
99
99
  <div
100
100
  key={index}
101
+ title={img.label}
101
102
  className={`aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group ${className || ''}`}
102
103
  onClick={() => lightbox.open(index)}
103
104
  >
@@ -141,10 +142,11 @@ export default function Gallery({
141
142
  {images.map((img, index) => imageElement(index, img, 'flex-1'))}
142
143
  </div>
143
144
  ) : (
144
- <div className="flex gap-3 overflow-x-auto pb-4">
145
+ <div className="flex gap-3 overflow-x-auto overscroll-x-contain pb-4">
145
146
  {images.map((img, index) => (
146
147
  <div
147
148
  key={index}
149
+ title={img.label}
148
150
  className="flex-none w-[30%] min-w-[250px] aspect-square overflow-hidden rounded-sm bg-muted/10 cursor-pointer group"
149
151
  onClick={() => lightbox.open(index)}
150
152
  >
package/src/index.css CHANGED
@@ -46,8 +46,8 @@
46
46
  --popover: 0 0% 100%;
47
47
  --popover-foreground: 240 10% 10%;
48
48
 
49
- /* Cyan/teal accent - technical, precise */
50
- --primary: 190 80% 42%;
49
+ /* Zinc accent - clean, neutral */
50
+ --primary: 240 5% 34%;
51
51
  --primary-foreground: 0 0% 100%;
52
52
 
53
53
  --secondary: 220 14% 96%;
@@ -62,18 +62,18 @@
62
62
 
63
63
  --border: 220 13% 90%;
64
64
  --input: 220 13% 90%;
65
- --ring: 190 80% 42%;
65
+ --ring: 240 5% 34%;
66
66
  --radius: 0.375rem;
67
67
 
68
68
  /* Sidebar */
69
69
  --sidebar-background: 220 14% 98%;
70
70
  --sidebar-foreground: 220 10% 40%;
71
- --sidebar-primary: 190 80% 42%;
71
+ --sidebar-primary: 240 5% 34%;
72
72
  --sidebar-primary-foreground: 0 0% 100%;
73
73
  --sidebar-accent: 220 14% 95%;
74
74
  --sidebar-accent-foreground: 240 10% 10%;
75
75
  --sidebar-border: 220 13% 91%;
76
- --sidebar-ring: 190 80% 42%;
76
+ --sidebar-ring: 240 5% 34%;
77
77
 
78
78
  /* Layout widths - generous for galleries */
79
79
  --content-width: 44rem;
@@ -92,9 +92,9 @@
92
92
  --popover: 0 0% 10%;
93
93
  --popover-foreground: 0 0% 93%;
94
94
 
95
- /* Cyan accent - crisp against zinc */
96
- --primary: 175 70% 45%;
97
- --primary-foreground: 0 0% 98%;
95
+ /* Zinc accent - clean, neutral */
96
+ --primary: 240 5% 65%;
97
+ --primary-foreground: 0 0% 7%;
98
98
 
99
99
  --secondary: 0 0% 14%;
100
100
  --secondary-foreground: 0 0% 80%;
@@ -108,17 +108,17 @@
108
108
 
109
109
  --border: 0 0% 22%;
110
110
  --input: 0 0% 22%;
111
- --ring: 175 70% 45%;
111
+ --ring: 240 5% 65%;
112
112
 
113
113
  /* Sidebar - zinc */
114
114
  --sidebar-background: 0 0% 10%;
115
115
  --sidebar-foreground: 0 0% 65%;
116
- --sidebar-primary: 175 70% 45%;
117
- --sidebar-primary-foreground: 0 0% 98%;
116
+ --sidebar-primary: 240 5% 65%;
117
+ --sidebar-primary-foreground: 0 0% 7%;
118
118
  --sidebar-accent: 0 0% 13%;
119
119
  --sidebar-accent-foreground: 0 0% 93%;
120
120
  --sidebar-border: 0 0% 19%;
121
- --sidebar-ring: 175 70% 45%;
121
+ --sidebar-ring: 240 5% 65%;
122
122
  }
123
123
 
124
124
  @layer base {
@@ -86,9 +86,7 @@ export function formatNumber(value: number): string {
86
86
  if (Math.abs(value) < 0.0001 && value !== 0) return value.toExponential(1);
87
87
  if (Math.abs(value) >= 10000) return value.toExponential(1);
88
88
  if (Number.isInteger(value)) return value.toString();
89
- const str = value.toString();
90
- if (str.length > 6) return value.toPrecision(4);
91
- return str;
89
+ return value.toFixed(1);
92
90
  }
93
91
 
94
92
  export function formatValue(value: ParameterValue): string {