@chiselandco/nexus 3.1.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Self-contained project portfolio components for Next.js App Router. Pass a `clientSlug`, `apiBase`, and `apiKey` — each component fetches, caches, and renders everything it needs with no client-side waterfall requests.
4
4
 
5
- **Version:** 3.1.0
5
+ **Version:** 3.1.1
6
6
 
7
7
  ---
8
8
 
@@ -474,7 +474,7 @@ export function Nav() {
474
474
  | `dataUrl` | `string` | No* | — | URL of a local API route created with `createMenuHandler()` — recommended for production |
475
475
  | `clientSlug` | `string` | No* | — | Client slug for direct fetch mode |
476
476
  | `apiBase` | `string` | No* | — | API base URL for direct fetch mode |
477
- | `apiKey` | `string` | No* | | Client API key for direct fetch mode |
477
+ | `apiKey` | `string` | No* | ��� | Client API key for direct fetch mode |
478
478
  | `menuId` | `string` | No | — | Slug of a curated menu |
479
479
  | `basePath` | `string` | Yes | — | Base path for project detail links |
480
480
  | `viewAllPath` | `string` | Yes | — | Path for the "View All Projects" link |
@@ -670,6 +670,20 @@ revalidateTag("chisel-menu-your-client-slug-main-nav")
670
670
 
671
671
  ---
672
672
 
673
+ ## Image optimisation
674
+
675
+ All components that render images use the built-in `thumbUrl()` helper to request appropriately-sized variants from Vercel Blob instead of loading full-resolution originals. The helper appends `?w=<width>&q=<quality>` query params which Vercel Blob handles on-the-fly. For non-Blob image sources the URL is returned unchanged.
676
+
677
+ | Location | Display size | Requested size | Quality |
678
+ |---|---|---|---|
679
+ | `ProjectMenuClient` card thumbnails | 72×54px | 144px wide (2× retina) | 60% |
680
+ | `GalleryCarousel` thumbnail strip | 72px wide | 160px wide (2× retina) | 60% |
681
+ | `GalleryCarousel` main image | Full width | 1600px wide | 80% |
682
+
683
+ If you are hosting images outside Vercel Blob (e.g. Cloudinary, imgix, your own CDN), you can override image rendering by passing pre-transformed URLs in your media objects — `thumbUrl()` will pass them through untouched.
684
+
685
+ ---
686
+
673
687
  ## Publishing
674
688
 
675
689
  ```bash
@@ -1 +1 @@
1
- {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAe,MAAM,SAAS,CAAA;AASpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAgOD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAW,EAAE,EAAE,oBAAoB,4BA2V1F"}
1
+ {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAe,MAAM,SAAS,CAAA;AAUpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAgOD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAW,EAAE,EAAE,oBAAoB,4BA2V1F"}
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useMemo, useEffect, useCallback, useRef } from "react";
4
4
  import { useRouter, useSearchParams, usePathname } from "next/navigation";
5
+ import { thumbUrl } from "./types";
5
6
  const FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
6
7
  const ACCENT = "#C8872A";
7
8
  const INK = "#0f0f0f";
@@ -168,7 +169,7 @@ function FilterDropdown({ field, values, activeSlug, onSelect, }) {
168
169
  }
169
170
  // ─── Main component ────────────────────────────────────────────────────────
170
171
  export function GalleryCarousel({ images, projectTitle, schema = [] }) {
171
- var _a;
172
+ var _a, _b;
172
173
  const router = useRouter();
173
174
  const pathname = usePathname();
174
175
  const searchParams = useSearchParams();
@@ -286,7 +287,7 @@ export function GalleryCarousel({ images, projectTitle, schema = [] }) {
286
287
  cursor: "pointer",
287
288
  letterSpacing: "0.04em",
288
289
  textDecoration: "underline",
289
- }, children: "Clear filters" })] })), active && (_jsxs("div", { style: { position: "relative", width: "100%", aspectRatio: "16/9", backgroundColor: "#1a1916", overflow: "hidden" }, children: [_jsx("img", { src: active.url, alt: active.alt || projectTitle, style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", display: "block" } }), _jsx("div", { style: {
290
+ }, children: "Clear filters" })] })), active && (_jsxs("div", { style: { position: "relative", width: "100%", aspectRatio: "16/9", backgroundColor: "#1a1916", overflow: "hidden" }, children: [_jsx("img", { src: (_b = thumbUrl(active.url, 1600, 80)) !== null && _b !== void 0 ? _b : active.url, alt: active.alt || projectTitle, style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", display: "block" } }), _jsx("div", { style: {
290
291
  position: "absolute",
291
292
  inset: "50% 0 0 0",
292
293
  background: "linear-gradient(to bottom, transparent, rgba(0,0,0,0.52))",
@@ -344,7 +345,7 @@ export function GalleryCarousel({ images, projectTitle, schema = [] }) {
344
345
  marginTop: "6px",
345
346
  paddingBottom: "2px",
346
347
  }, children: filteredImages.map((img, i) => {
347
- var _a;
348
+ var _a, _b;
348
349
  const isActive = i === safeIndex;
349
350
  return (_jsx("button", { onClick: () => setCurrent(i), "aria-label": `View image ${i + 1}`, style: {
350
351
  position: "relative",
@@ -361,7 +362,7 @@ export function GalleryCarousel({ images, projectTitle, schema = [] }) {
361
362
  outlineOffset: isActive ? "1px" : "0",
362
363
  transition: "outline 0.12s, opacity 0.12s",
363
364
  opacity: isActive ? 1 : 0.55,
364
- }, children: _jsx("img", { src: img.url, alt: img.alt || `Image ${i + 1}`, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }) }, (_a = img.id) !== null && _a !== void 0 ? _a : i));
365
+ }, children: _jsx("img", { src: (_a = thumbUrl(img.url, 160, 60)) !== null && _a !== void 0 ? _a : img.url, alt: img.alt || `Image ${i + 1}`, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }) }, (_b = img.id) !== null && _b !== void 0 ? _b : i));
365
366
  }) }))] }));
366
367
  }
367
368
  function arrowBtn(side) {
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAa3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAkBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAe,EACf,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,qBAqfxB"}
1
+ {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAc3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAkBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAe,EACf,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,qBAqfxB"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from "react";
4
+ import { thumbUrl } from "./types";
4
5
  function parseSingleValue(raw) {
5
6
  if (typeof raw === "string")
6
7
  return raw.replace(/`/g, "").trim();
@@ -318,11 +319,11 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, apiKey, menuId
318
319
  margin: "0 0 16px 0",
319
320
  display: "none",
320
321
  }, className: "chisel-menu-subtitle", children: subtitle })), displayed.length === 0 ? (_jsx("p", { style: { fontSize: "14px", color: "#a1a1aa", margin: 0 }, children: "No projects found." })) : (_jsx("div", { className: "chisel-menu-card-grid", children: displayed.map((project) => {
321
- var _a, _b, _c, _d, _e, _f;
322
- const imageUrl = (_d = (_a = project.image_url) !== null && _a !== void 0 ? _a : (_c = (_b = project.media) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url) !== null && _d !== void 0 ? _d : null;
322
+ var _a, _b, _c, _d, _e;
323
+ const imageUrl = thumbUrl((_a = project.image_url) !== null && _a !== void 0 ? _a : (_c = (_b = project.media) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url, 144, 60);
323
324
  const badgeRaw = badgeField ? parseSingleValue(project.custom_field_values[badgeField.key]) : null;
324
- const badgeOptMap = badgeField ? ((_e = fieldOptionsMap[badgeField.key]) !== null && _e !== void 0 ? _e : {}) : {};
325
- const badge = badgeRaw ? ((_f = badgeOptMap[badgeRaw]) !== null && _f !== void 0 ? _f : badgeRaw) : null;
325
+ const badgeOptMap = badgeField ? ((_d = fieldOptionsMap[badgeField.key]) !== null && _d !== void 0 ? _d : {}) : {};
326
+ const badge = badgeRaw ? ((_e = badgeOptMap[badgeRaw]) !== null && _e !== void 0 ? _e : badgeRaw) : null;
326
327
  const tags = tagsField ? parseMultiValue(project.custom_field_values[tagsField.key]) : [];
327
328
  const href = `${basePath}/${project.slug}`;
328
329
  const isHovered = hoveredCard === project.id;
package/dist/types.d.ts CHANGED
@@ -30,6 +30,12 @@ export interface LocationValue {
30
30
  state?: string;
31
31
  }
32
32
  export type CustomFieldValue = string | number | string[] | LocationValue | null;
33
+ /**
34
+ * Returns a resized image URL for Vercel Blob-hosted images.
35
+ * Appends ?w=<width>&q=<quality> for on-the-fly resizing.
36
+ * Falls back to the original URL unchanged for non-Blob sources.
37
+ */
38
+ export declare function thumbUrl(url: string | null | undefined, width: number, quality?: number): string | null;
33
39
  export interface Project {
34
40
  id: string;
35
41
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,MAAM,GAAG,IAAI,CAanG;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
package/dist/types.js CHANGED
@@ -1 +1,21 @@
1
- export {};
1
+ /**
2
+ * Returns a resized image URL for Vercel Blob-hosted images.
3
+ * Appends ?w=<width>&q=<quality> for on-the-fly resizing.
4
+ * Falls back to the original URL unchanged for non-Blob sources.
5
+ */
6
+ export function thumbUrl(url, width, quality = 75) {
7
+ if (!url)
8
+ return null;
9
+ try {
10
+ const u = new URL(url);
11
+ if (u.hostname.endsWith(".public.blob.vercel-storage.com")) {
12
+ u.searchParams.set("w", String(width));
13
+ u.searchParams.set("q", String(quality));
14
+ return u.toString();
15
+ }
16
+ }
17
+ catch (_a) {
18
+ // not a valid absolute URL — return as-is
19
+ }
20
+ return url;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chiselandco/nexus",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "Self-contained project portfolio components for Next.js App Router. Includes ProjectPortfolio, ProjectPortfolioClient, ProjectDetail, SimilarProjects, ProjectMenu, ProjectMenuClient, GalleryCarousel, and FilterSidebar. Pass a clientSlug and apiBase — done.",
5
5
  "keywords": [
6
6
  "nextjs",