@shopify/shop-minis-react 0.0.33 → 0.0.34

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.
Files changed (51) hide show
  1. package/dist/_virtual/index10.js +2 -2
  2. package/dist/_virtual/index2.js +4 -4
  3. package/dist/_virtual/index3.js +4 -4
  4. package/dist/_virtual/index4.js +2 -2
  5. package/dist/_virtual/index5.js +3 -2
  6. package/dist/_virtual/index5.js.map +1 -1
  7. package/dist/_virtual/index6.js +2 -2
  8. package/dist/_virtual/index7.js +2 -3
  9. package/dist/_virtual/index7.js.map +1 -1
  10. package/dist/_virtual/index8.js +2 -2
  11. package/dist/_virtual/index9.js +2 -2
  12. package/dist/components/atoms/image.js +52 -0
  13. package/dist/components/atoms/image.js.map +1 -0
  14. package/dist/components/commerce/merchant-card.js +1 -1
  15. package/dist/components/commerce/merchant-card.js.map +1 -1
  16. package/dist/components/commerce/product-card.js +11 -11
  17. package/dist/components/commerce/product-card.js.map +1 -1
  18. package/dist/components/content/image-content-wrapper.js +29 -22
  19. package/dist/components/content/image-content-wrapper.js.map +1 -1
  20. package/dist/hooks/content/useCreateImageContent.js +16 -22
  21. package/dist/hooks/content/useCreateImageContent.js.map +1 -1
  22. package/dist/hooks/storage/useImageUpload.js +36 -37
  23. package/dist/hooks/storage/useImageUpload.js.map +1 -1
  24. package/dist/index.js +56 -54
  25. package/dist/shop-minis-platform/src/types/content.js.map +1 -1
  26. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
  27. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +1 -1
  28. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
  29. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +1 -1
  30. package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
  31. package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  32. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
  33. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
  34. package/dist/utils/colors.js +1 -1
  35. package/dist/utils/image.js +45 -9
  36. package/dist/utils/image.js.map +1 -1
  37. package/package.json +2 -2
  38. package/src/components/atoms/{thumbhash-image.tsx → image.tsx} +14 -14
  39. package/src/components/commerce/merchant-card.tsx +2 -2
  40. package/src/components/commerce/product-card.tsx +2 -2
  41. package/src/components/content/image-content-wrapper.tsx +9 -2
  42. package/src/components/index.ts +1 -1
  43. package/src/hooks/content/useCreateImageContent.ts +1 -7
  44. package/src/hooks/storage/useImageUpload.ts +22 -20
  45. package/src/utils/image.ts +72 -0
  46. package/src/utils/index.ts +1 -1
  47. package/dist/components/atoms/thumbhash-image.js +0 -54
  48. package/dist/components/atoms/thumbhash-image.js.map +0 -1
  49. package/dist/utils/imageToDataUri.js +0 -10
  50. package/dist/utils/imageToDataUri.js.map +0 -1
  51. package/src/utils/imageToDataUri.ts +0 -8
@@ -1,51 +1,50 @@
1
1
  import { useCallback as m } from "react";
2
- import { useShopActions as u } from "../../internal/useShopActions.js";
2
+ import { useShopActions as c } from "../../internal/useShopActions.js";
3
+ import { fileToDataUri as u } from "../../utils/image.js";
3
4
  const d = async (e) => {
4
- const r = await (await fetch(e.uri)).blob();
5
+ const r = await u(e), o = await (await fetch(r)).blob();
5
6
  return {
6
- ...e,
7
- fileSize: e.fileSize ?? r.size,
8
- fileBlob: r
7
+ mimeType: e.type,
8
+ fileSize: e.size ?? o.size,
9
+ fileBlob: o
9
10
  };
10
- }, f = async (e, o) => {
11
- const r = new FormData();
12
- o.parameters.forEach(({ name: l, value: t }) => {
13
- r.append(l, t);
14
- }), r.append("file", e.fileBlob);
15
- const a = await fetch(o.url, {
11
+ }, f = async (e, r) => {
12
+ const t = new FormData();
13
+ r.parameters.forEach(({ name: s, value: a }) => {
14
+ t.append(s, a);
15
+ }), t.append("file", e.fileBlob);
16
+ const o = await fetch(r.url, {
16
17
  method: "POST",
17
- body: r
18
+ body: t
18
19
  });
19
- return a.ok ? {} : (console.error("Failed to upload image", {
20
- response: await a.text()
20
+ return o.ok ? {} : (console.error("Failed to upload image", {
21
+ response: await o.text()
21
22
  }), { error: "Failed to upload image" });
22
- }, h = () => {
23
- const { createImageUploadLink: e, completeImageUpload: o } = u();
23
+ }, U = () => {
24
+ const { createImageUploadLink: e, completeImageUpload: r } = c();
24
25
  return {
25
26
  uploadImage: m(
26
- async (a) => {
27
- if (a.length > 1)
28
- throw new Error("Multiple image upload is not supported yet");
29
- const l = a[0], t = await d(l), s = await e({
27
+ async (o) => {
28
+ const s = await d(o), a = await e({
30
29
  input: [
31
30
  {
32
- mimeType: t.mimeType,
33
- fileSize: t.fileSize
31
+ mimeType: s.mimeType,
32
+ fileSize: s.fileSize
34
33
  }
35
34
  ]
36
35
  });
37
- if (!s.ok)
38
- throw new Error(s.error.message);
39
- const { error: p } = await f(
40
- t,
41
- s?.data?.targets?.[0]
36
+ if (!a.ok)
37
+ throw new Error(a.error.message);
38
+ const { error: n } = await f(
39
+ s,
40
+ a?.data?.targets?.[0]
42
41
  );
43
- if (p)
44
- throw new Error(p);
45
- let c = 0;
46
- for (; c < 30; ) {
47
- const i = await o({
48
- resourceUrls: s?.data?.targets?.map((n) => n.resourceUrl) || []
42
+ if (n)
43
+ throw new Error(n);
44
+ let p = 0;
45
+ for (; p < 30; ) {
46
+ const i = await r({
47
+ resourceUrls: a?.data?.targets?.map((l) => l.resourceUrl) || []
49
48
  });
50
49
  if (!i.ok)
51
50
  throw new Error(i.error.message);
@@ -54,18 +53,18 @@ const d = async (e) => {
54
53
  {
55
54
  id: i.data.files[0].id,
56
55
  imageUrl: i.data.files[0].image?.url,
57
- resourceUrl: s?.data?.targets?.[0]?.resourceUrl
56
+ resourceUrl: a?.data?.targets?.[0]?.resourceUrl
58
57
  }
59
58
  ];
60
- await new Promise((n) => setTimeout(n, 1e3)), c++;
59
+ await new Promise((l) => setTimeout(l, 1e3)), p++;
61
60
  }
62
61
  throw new Error("Image upload completion timed out");
63
62
  },
64
- [e, o]
63
+ [e, r]
65
64
  )
66
65
  };
67
66
  };
68
67
  export {
69
- h as useImageUpload
68
+ U as useImageUpload
70
69
  };
71
70
  //# sourceMappingURL=useImageUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize?: number\n /**\n * The URI of the image to upload.\n */\n uri: string\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image attached to the current user.\n */\n uploadImage: (params: UploadImageParams[]) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: UploadImageParams) => {\n const response = await fetch(image.uri)\n const blob = await response.blob()\n\n return {\n ...image,\n fileSize: image.fileSize ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (\n image: UploadImageParams & {fileSize: number; fileBlob: Blob},\n target: UploadTarget\n) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (params: UploadImageParams[]) => {\n if (params.length > 1) {\n throw new Error('Multiple image upload is not supported yet')\n }\n\n const imageParams = params[0]\n const processedImageParams = await processFileData(imageParams)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","params","imageParams","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;AA6CA,MAAMA,IAAkB,OAAOC,MAA6B;AAEpD,QAAAC,IAAO,OADI,MAAM,MAAMD,EAAM,GAAG,GACV,KAAK;AAE1B,SAAA;AAAA,IACL,GAAGA;AAAA,IACH,UAAUA,EAAM,YAAYC,EAAK;AAAA,IACjC,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OACtBF,GACAG,MACG;AACG,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQJ,EAAM,QAAQ;AAEtC,QAAMO,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AAiE7D,SAAA;AAAA,IACL,aAhEkBC;AAAA,MAClB,OAAOC,MAAgC;AACjC,YAAAA,EAAO,SAAS;AACZ,gBAAA,IAAI,MAAM,4CAA4C;AAGxD,cAAAC,IAAcD,EAAO,CAAC,GACtBE,IAAuB,MAAMhB,EAAgBe,CAAW,GAExDE,IAAQ,MAAMP,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUM,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAIrC,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMf;AAAA,UACjCa;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMT,EAAoB;AAAA,YACvC,cACEM,GAAO,MAAM,SAAS,IAAI,CAAUb,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACgB,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACT,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
1
+ {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\nimport {fileToDataUri} from '../../utils'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The file to upload.\n */\n image: File\n}\n\ninterface ProcessedImage {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize: number\n /**\n * The file blob of the image.\n */\n fileBlob: Blob\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image which will be attached to the current user.\n */\n uploadImage: (image: File) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: File): Promise<ProcessedImage> => {\n const uri = await fileToDataUri(image)\n\n const response = await fetch(uri)\n const blob = await response.blob()\n\n return {\n mimeType: image.type,\n fileSize: image.size ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (image: ProcessedImage, target: UploadTarget) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (image: File) => {\n const processedImageParams = await processFileData(image)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","uri","fileToDataUri","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;;AAqDA,MAAMA,IAAkB,OAAOC,MAAyC;AAChE,QAAAC,IAAM,MAAMC,EAAcF,CAAK,GAG/BG,IAAO,OADI,MAAM,MAAMF,CAAG,GACJ,KAAK;AAE1B,SAAA;AAAA,IACL,UAAUD,EAAM;AAAA,IAChB,UAAUA,EAAM,QAAQG,EAAK;AAAA,IAC7B,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OAAOJ,GAAuBK,MAAyB;AACvE,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQN,EAAM,QAAQ;AAEtC,QAAMS,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AA4D7D,SAAA;AAAA,IACL,aA3DkBC;AAAA,MAClB,OAAOd,MAAgB;AACf,cAAAe,IAAuB,MAAMhB,EAAgBC,CAAK,GAElDgB,IAAQ,MAAML,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUI,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAIrC,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMb;AAAA,UACjCW;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMP,EAAoB;AAAA,YACvC,cACEI,GAAO,MAAM,SAAS,IAAI,CAAUX,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACc,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACP,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
package/dist/index.js CHANGED
@@ -3,17 +3,17 @@ import { MinisContainer as i } from "./components/MinisContainer.js";
3
3
  import { ProductCard as n, ProductCardBadge as m, ProductCardContainer as l, ProductCardFavoriteButton as s, ProductCardImage as u, ProductCardImageContainer as f, ProductCardInfo as c, ProductCardPrice as x, ProductCardTitle as d } from "./components/commerce/product-card.js";
4
4
  import { ProductLink as g } from "./components/commerce/product-link.js";
5
5
  import { MerchantCard as D, MerchantCardContainer as P, MerchantCardHeader as A, MerchantCardInfo as h, MerchantCardName as T, MerchantCardRating as I } from "./components/commerce/merchant-card.js";
6
- import { ProductCardSkeleton as w } from "./components/commerce/product-card-skeleton.js";
6
+ import { ProductCardSkeleton as v } from "./components/commerce/product-card-skeleton.js";
7
7
  import { MerchantCardSkeleton as M } from "./components/commerce/merchant-card-skeleton.js";
8
- import { QuantitySelector as B } from "./components/commerce/quantity-selector.js";
9
- import { Search as F, SearchInput as b, SearchProvider as U, SearchResultsList as L } from "./components/commerce/search.js";
8
+ import { QuantitySelector as k } from "./components/commerce/quantity-selector.js";
9
+ import { Search as E, SearchInput as F, SearchProvider as b, SearchResultsList as L } from "./components/commerce/search.js";
10
10
  import { ImageContentWrapper as y } from "./components/content/image-content-wrapper.js";
11
11
  import { MinisRouter as H } from "./components/navigation/minis-router.js";
12
12
  import { TransitionLink as V } from "./components/navigation/transition-link.js";
13
13
  import { Button as _ } from "./components/atoms/button.js";
14
14
  import { FavoriteButton as Y } from "./components/atoms/favorite-button.js";
15
15
  import { IconButton as j } from "./components/atoms/icon-button.js";
16
- import { ThumbhashImage as J } from "./components/atoms/thumbhash-image.js";
16
+ import { Image as J } from "./components/atoms/image.js";
17
17
  import { Touchable as X } from "./components/atoms/touchable.js";
18
18
  import { LongPressDetector as $ } from "./components/atoms/long-press-detector.js";
19
19
  import { AlertDialogAtom as er } from "./components/atoms/alert-dialog.js";
@@ -21,9 +21,9 @@ import { List as tr } from "./components/atoms/list.js";
21
21
  import { VideoPlayer as ir } from "./components/atoms/video-player.js";
22
22
  import { Accordion as nr, AccordionContent as mr, AccordionItem as lr, AccordionTrigger as sr } from "./components/ui/accordion.js";
23
23
  import { Alert as fr, AlertDescription as cr, AlertTitle as xr } from "./components/ui/alert.js";
24
- import { AlertDialog as Cr, AlertDialogAction as gr, AlertDialogCancel as Sr, AlertDialogContent as Dr, AlertDialogDescription as Pr, AlertDialogFooter as Ar, AlertDialogHeader as hr, AlertDialogOverlay as Tr, AlertDialogPortal as Ir, AlertDialogTitle as vr, AlertDialogTrigger as wr } from "./components/ui/alert-dialog.js";
25
- import { Avatar as Mr, AvatarFallback as kr, AvatarImage as Br } from "./components/ui/avatar.js";
26
- import { Badge as Fr, badgeVariants as br } from "./components/ui/badge.js";
24
+ import { AlertDialog as Cr, AlertDialogAction as gr, AlertDialogCancel as Sr, AlertDialogContent as Dr, AlertDialogDescription as Pr, AlertDialogFooter as Ar, AlertDialogHeader as hr, AlertDialogOverlay as Tr, AlertDialogPortal as Ir, AlertDialogTitle as Rr, AlertDialogTrigger as vr } from "./components/ui/alert-dialog.js";
25
+ import { Avatar as Mr, AvatarFallback as Ur, AvatarImage as kr } from "./components/ui/avatar.js";
26
+ import { Badge as Er, badgeVariants as Fr } from "./components/ui/badge.js";
27
27
  import { Card as Lr, CardAction as Nr, CardContent as yr, CardDescription as Gr, CardFooter as Hr, CardHeader as Or, CardTitle as Vr } from "./components/ui/card.js";
28
28
  import { Carousel as _r, CarouselContent as Wr, CarouselItem as Yr, CarouselNext as Qr, CarouselPrevious as jr } from "./components/ui/carousel.js";
29
29
  import { Checkbox as Jr } from "./components/ui/checkbox.js";
@@ -32,9 +32,9 @@ import { Drawer as me, DrawerClose as le, DrawerContent as se, DrawerDescription
32
32
  import { Input as De } from "./components/ui/input.js";
33
33
  import { Label as Ae } from "./components/ui/label.js";
34
34
  import { Progress as Te } from "./components/ui/progress.js";
35
- import { RadioGroup as ve, RadioGroupItem as we } from "./components/ui/radio-group.js";
36
- import { ResizableHandle as Me, ResizablePanel as ke, ResizablePanelGroup as Be } from "./components/ui/resizable.js";
37
- import { ScrollArea as Fe, ScrollBar as be } from "./components/ui/scroll-area.js";
35
+ import { RadioGroup as Re, RadioGroupItem as ve } from "./components/ui/radio-group.js";
36
+ import { ResizableHandle as Me, ResizablePanel as Ue, ResizablePanelGroup as ke } from "./components/ui/resizable.js";
37
+ import { ScrollArea as Ee, ScrollBar as Fe } from "./components/ui/scroll-area.js";
38
38
  import { Select as Le, SelectContent as Ne, SelectGroup as ye, SelectItem as Ge, SelectLabel as He, SelectScrollDownButton as Oe, SelectScrollUpButton as Ve, SelectSeparator as ze, SelectTrigger as _e, SelectValue as We } from "./components/ui/select.js";
39
39
  import { Separator as Qe } from "./components/ui/separator.js";
40
40
  import { Sheet as qe, SheetClose as Je, SheetContent as Ke, SheetDescription as Xe, SheetFooter as Ze, SheetHeader as $e, SheetTitle as ro, SheetTrigger as eo } from "./components/ui/sheet.js";
@@ -49,11 +49,11 @@ import { useFollowedShopsActions as So } from "./hooks/user/useFollowedShopsActi
49
49
  import { useCurrentUser as Po } from "./hooks/user/useCurrentUser.js";
50
50
  import { useOrders as ho } from "./hooks/user/useOrders.js";
51
51
  import { useBuyerAttributes as Io } from "./hooks/user/useBuyerAttributes.js";
52
- import { useGenerateUserToken as wo } from "./hooks/user/useGenerateUserToken.js";
52
+ import { useGenerateUserToken as vo } from "./hooks/user/useGenerateUserToken.js";
53
53
  import { useProductListActions as Mo } from "./hooks/product/useProductListActions.js";
54
- import { useProductLists as Bo } from "./hooks/product/useProductLists.js";
55
- import { useProductList as Fo } from "./hooks/product/useProductList.js";
56
- import { useProduct as Uo } from "./hooks/product/useProduct.js";
54
+ import { useProductLists as ko } from "./hooks/product/useProductLists.js";
55
+ import { useProductList as Eo } from "./hooks/product/useProductList.js";
56
+ import { useProduct as bo } from "./hooks/product/useProduct.js";
57
57
  import { useProducts as No } from "./hooks/product/useProducts.js";
58
58
  import { useProductVariants as Go } from "./hooks/product/useProductVariants.js";
59
59
  import { useProductMedia as Oo } from "./hooks/product/useProductMedia.js";
@@ -76,13 +76,13 @@ import { useErrorToast as St } from "./hooks/util/useErrorToast.js";
76
76
  import { useErrorScreen as Pt } from "./hooks/util/useErrorScreen.js";
77
77
  import { useShare as ht } from "./hooks/util/useShare.js";
78
78
  import { useImagePicker as It } from "./hooks/util/useImagePicker.js";
79
- import { MiniEntityNotFoundError as wt, MiniError as Rt, MiniNetworkError as Mt, formatError as kt } from "./utils/errors.js";
80
- import { extractBrandTheme as Et, formatReviewCount as Ft, getFeaturedImages as bt, normalizeRating as Ut } from "./utils/merchant-card.js";
79
+ import { MiniEntityNotFoundError as vt, MiniError as wt, MiniNetworkError as Mt, formatError as Ut } from "./utils/errors.js";
80
+ import { extractBrandTheme as Bt, formatReviewCount as Et, getFeaturedImages as Ft, normalizeRating as bt } from "./utils/merchant-card.js";
81
81
  import { parseUrl as Nt } from "./utils/parseUrl.js";
82
- import { fileToDataUri as Gt } from "./utils/imageToDataUri.js";
83
- import { UserState as Ot, UserTokenGenerateUserErrorCode as Vt } from "./shop-minis-platform/src/types/user.js";
84
- import { ContentCreateUserErrorCode as _t } from "./shop-minis-platform/src/types/content.js";
85
- import { Consent as Yt, ConsentStatus as Qt } from "./shop-minis-platform/src/types/permissions.js";
82
+ import { fileToDataUri as Gt, getResizedImageUrl as Ht, getThumbhashDataURL as Ot } from "./utils/image.js";
83
+ import { UserState as zt, UserTokenGenerateUserErrorCode as _t } from "./shop-minis-platform/src/types/user.js";
84
+ import { ContentCreateUserErrorCode as Yt } from "./shop-minis-platform/src/types/content.js";
85
+ import { Consent as jt, ConsentStatus as qt } from "./shop-minis-platform/src/types/permissions.js";
86
86
  export {
87
87
  nr as Accordion,
88
88
  mr as AccordionContent,
@@ -100,13 +100,13 @@ export {
100
100
  hr as AlertDialogHeader,
101
101
  Tr as AlertDialogOverlay,
102
102
  Ir as AlertDialogPortal,
103
- vr as AlertDialogTitle,
104
- wr as AlertDialogTrigger,
103
+ Rr as AlertDialogTitle,
104
+ vr as AlertDialogTrigger,
105
105
  xr as AlertTitle,
106
106
  Mr as Avatar,
107
- kr as AvatarFallback,
108
- Br as AvatarImage,
109
- Fr as Badge,
107
+ Ur as AvatarFallback,
108
+ kr as AvatarImage,
109
+ Er as Badge,
110
110
  _ as Button,
111
111
  Lr as Card,
112
112
  Nr as CardAction,
@@ -121,9 +121,9 @@ export {
121
121
  Qr as CarouselNext,
122
122
  jr as CarouselPrevious,
123
123
  Jr as Checkbox,
124
- Yt as Consent,
125
- Qt as ConsentStatus,
126
- _t as ContentCreateUserErrorCode,
124
+ jt as Consent,
125
+ qt as ConsentStatus,
126
+ Yt as ContentCreateUserErrorCode,
127
127
  o as DATA_NAVIGATION_TYPE_ATTRIBUTE,
128
128
  Xr as Dialog,
129
129
  Zr as DialogClose,
@@ -147,6 +147,7 @@ export {
147
147
  ge as DrawerTrigger,
148
148
  Y as FavoriteButton,
149
149
  j as IconButton,
150
+ J as Image,
150
151
  y as ImageContentWrapper,
151
152
  De as Input,
152
153
  Ae as Label,
@@ -159,8 +160,8 @@ export {
159
160
  T as MerchantCardName,
160
161
  I as MerchantCardRating,
161
162
  M as MerchantCardSkeleton,
162
- wt as MiniEntityNotFoundError,
163
- Rt as MiniError,
163
+ vt as MiniEntityNotFoundError,
164
+ wt as MiniError,
164
165
  Mt as MiniNetworkError,
165
166
  i as MinisContainer,
166
167
  H as MinisRouter,
@@ -173,21 +174,21 @@ export {
173
174
  f as ProductCardImageContainer,
174
175
  c as ProductCardInfo,
175
176
  x as ProductCardPrice,
176
- w as ProductCardSkeleton,
177
+ v as ProductCardSkeleton,
177
178
  d as ProductCardTitle,
178
179
  g as ProductLink,
179
180
  Te as Progress,
180
- B as QuantitySelector,
181
- ve as RadioGroup,
182
- we as RadioGroupItem,
181
+ k as QuantitySelector,
182
+ Re as RadioGroup,
183
+ ve as RadioGroupItem,
183
184
  Me as ResizableHandle,
184
- ke as ResizablePanel,
185
- Be as ResizablePanelGroup,
186
- Fe as ScrollArea,
187
- be as ScrollBar,
188
- F as Search,
189
- b as SearchInput,
190
- U as SearchProvider,
185
+ Ue as ResizablePanel,
186
+ ke as ResizablePanelGroup,
187
+ Ee as ScrollArea,
188
+ Fe as ScrollBar,
189
+ E as Search,
190
+ F as SearchInput,
191
+ b as SearchProvider,
191
192
  L as SearchResultsList,
192
193
  Le as Select,
193
194
  Ne as SelectContent,
@@ -209,20 +210,21 @@ export {
209
210
  ro as SheetTitle,
210
211
  eo as SheetTrigger,
211
212
  io as Skeleton,
212
- J as ThumbhashImage,
213
213
  to as Toaster,
214
214
  X as Touchable,
215
215
  V as TransitionLink,
216
- Ot as UserState,
217
- Vt as UserTokenGenerateUserErrorCode,
216
+ zt as UserState,
217
+ _t as UserTokenGenerateUserErrorCode,
218
218
  ir as VideoPlayer,
219
- br as badgeVariants,
220
- Et as extractBrandTheme,
219
+ Fr as badgeVariants,
220
+ Bt as extractBrandTheme,
221
221
  Gt as fileToDataUri,
222
- kt as formatError,
223
- Ft as formatReviewCount,
224
- bt as getFeaturedImages,
225
- Ut as normalizeRating,
222
+ Ut as formatError,
223
+ Et as formatReviewCount,
224
+ Ft as getFeaturedImages,
225
+ Ht as getResizedImageUrl,
226
+ Ot as getThumbhashDataURL,
227
+ bt as normalizeRating,
226
228
  Nt as parseUrl,
227
229
  Ko as useAsyncStorage,
228
230
  Io as useBuyerAttributes,
@@ -235,16 +237,16 @@ export {
235
237
  St as useErrorToast,
236
238
  Co as useFollowedShops,
237
239
  So as useFollowedShopsActions,
238
- wo as useGenerateUserToken,
240
+ vo as useGenerateUserToken,
239
241
  It as useImagePicker,
240
242
  rt as useImageUpload,
241
243
  mt as useNavigateWithTransition,
242
244
  ho as useOrders,
243
245
  Qo as usePopularProducts,
244
- Uo as useProduct,
245
- Fo as useProductList,
246
+ bo as useProduct,
247
+ Eo as useProductList,
246
248
  Mo as useProductListActions,
247
- Bo as useProductLists,
249
+ ko as useProductLists,
248
250
  Oo as useProductMedia,
249
251
  zo as useProductSearch,
250
252
  Go as useProductVariants,
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sources":["../../../../../shop-minis-platform/src/types/content.ts"],"sourcesContent":["/* eslint-disable @shopify/typescript/prefer-pascal-case-enums */\nexport interface Image {\n url: string\n altText?: string | null\n height?: number | null\n width?: number | null\n /**\n * @deprecated This property will be removed in a future version\n */\n sensitive: boolean\n thumbhash?: string | null\n}\n\nexport interface ContentImage {\n id?: string | null\n url: string\n width?: number | null\n height?: number | null\n}\n\nexport interface ContentProduct {\n id: string\n title: string\n featuredImage?: ContentImage | null\n}\n\nexport type ContentVisibility = 'DISCOVERABLE' | 'LINKABLE'\n\nexport interface Content {\n publicId: string\n externalId?: string | null\n image: ContentImage\n title: string\n description?: string | null\n visibility: ContentVisibility[]\n shareableUrl?: string | null\n products?: ContentProduct[] | null\n}\n\nexport enum ContentCreateUserErrorCode {\n DUPLICATE_EXTERNAL_ID = 'DUPLICATE_EXTERNAL_ID',\n INELIGIBLE_PRODUCTS = 'INELIGIBLE_PRODUCTS',\n}\n\nexport interface ContentCreateUserErrors {\n code: ContentCreateUserErrorCode\n message: string\n}\n\nexport interface ContentIdentifierInput {\n externalId?: string | null\n publicId?: string | null\n}\n"],"names":["ContentCreateUserErrorCode"],"mappings":"AAuCY,IAAAA,sBAAAA,OACVA,EAAA,wBAAwB,yBACxBA,EAAA,sBAAsB,uBAFZA,IAAAA,KAAA,CAAA,CAAA;"}
1
+ {"version":3,"file":"content.js","sources":["../../../../../shop-minis-platform/src/types/content.ts"],"sourcesContent":["/* eslint-disable @shopify/typescript/prefer-pascal-case-enums */\nexport interface ImageType {\n url: string\n altText?: string | null\n height?: number | null\n width?: number | null\n /**\n * @deprecated This property will be removed in a future version\n */\n sensitive: boolean\n thumbhash?: string | null\n}\n\nexport interface ContentImage {\n id?: string | null\n url: string\n width?: number | null\n height?: number | null\n thumbhash?: string | null\n altText?: string | null\n}\n\nexport interface ContentProduct {\n id: string\n title: string\n featuredImage?: ContentImage | null\n}\n\nexport type ContentVisibility = 'DISCOVERABLE' | 'LINKABLE'\n\nexport interface Content {\n publicId: string\n externalId?: string | null\n image: ContentImage\n title: string\n description?: string | null\n visibility: ContentVisibility[]\n shareableUrl?: string | null\n products?: ContentProduct[] | null\n}\n\nexport enum ContentCreateUserErrorCode {\n DUPLICATE_EXTERNAL_ID = 'DUPLICATE_EXTERNAL_ID',\n INELIGIBLE_PRODUCTS = 'INELIGIBLE_PRODUCTS',\n}\n\nexport interface ContentCreateUserErrors {\n code: ContentCreateUserErrorCode\n message: string\n}\n\nexport interface ContentIdentifierInput {\n externalId?: string | null\n publicId?: string | null\n}\n"],"names":["ContentCreateUserErrorCode"],"mappings":"AAyCY,IAAAA,sBAAAA,OACVA,EAAA,wBAAwB,yBACxBA,EAAA,sBAAsB,uBAFZA,IAAAA,KAAA,CAAA,CAAA;"}
@@ -1,4 +1,4 @@
1
- import { s as r } from "../../../../../../../../_virtual/index7.js";
1
+ import { s as r } from "../../../../../../../../_virtual/index6.js";
2
2
  function s() {
3
3
  return r.useSyncExternalStore(
4
4
  e,
@@ -1,4 +1,4 @@
1
- import { __module as q } from "../../../../../../../../_virtual/index5.js";
1
+ import { __module as q } from "../../../../../../../../_virtual/index4.js";
2
2
  import { __require as F } from "../../../../../global@4.4.0/node_modules/global/window.js";
3
3
  import { __require as N } from "../../../../../@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/extends.js";
4
4
  import { __require as J } from "../../../../../is-function@1.0.2/node_modules/is-function/index.js";
@@ -1,4 +1,4 @@
1
- import { __exports as r } from "../../../../../../../../_virtual/index8.js";
1
+ import { __exports as r } from "../../../../../../../../_virtual/index9.js";
2
2
  import { __require as a } from "./dom.js";
3
3
  import { __require as o } from "./dom-parser.js";
4
4
  var i;
@@ -1,4 +1,4 @@
1
- import { __module as p } from "../../../../../../_virtual/index10.js";
1
+ import { __module as p } from "../../../../../../_virtual/index8.js";
2
2
  import { __require as F } from "../../../color-name@1.1.4/node_modules/color-name/index.js";
3
3
  import { __require as x } from "../../../simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js";
4
4
  var M;
@@ -2,7 +2,7 @@ import L from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-ut
2
2
  import T from "../../../../../../../_virtual/window.js";
3
3
  import { forEachMediaGroup as Z } from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-groups.js";
4
4
  import J from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/decode-b64-to-uint8-array.js";
5
- import { l as Q } from "../../../../../../../_virtual/index6.js";
5
+ import { l as Q } from "../../../../../../../_virtual/index5.js";
6
6
  /*! @name mpd-parser @version 1.3.1 @license Apache-2.0 */
7
7
  const w = (e) => !!e && typeof e == "object", E = (...e) => e.reduce((n, t) => (typeof t != "object" || Object.keys(t).forEach((r) => {
8
8
  Array.isArray(n[r]) && Array.isArray(t[r]) ? n[r] = n[r].concat(t[r]) : w(n[r]) && w(t[r]) ? n[r] = E(n[r], t[r]) : n[r] = t[r];
@@ -1,4 +1,4 @@
1
- import { __exports as i } from "../../../../../../_virtual/index4.js";
1
+ import { __exports as i } from "../../../../../../_virtual/index7.js";
2
2
  var c;
3
3
  function d() {
4
4
  if (c) return i;
@@ -1,4 +1,4 @@
1
- import { __module as r } from "../../../../../../../_virtual/index9.js";
1
+ import { __module as r } from "../../../../../../../_virtual/index10.js";
2
2
  import { __require as o } from "../cjs/use-sync-external-store-shim.production.js";
3
3
  import { __require as i } from "../cjs/use-sync-external-store-shim.development.js";
4
4
  var e;
@@ -1,6 +1,6 @@
1
1
  import S from "../../../../../../../_virtual/window.js";
2
2
  import B from "../../../../../../../_virtual/document.js";
3
- import il from "../../../../../../../_virtual/index2.js";
3
+ import il from "../../../../../../../_virtual/index3.js";
4
4
  import ao from "../../../../../../../_virtual/browser-index.js";
5
5
  import xe from "../../../../@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/extends.js";
6
6
  import Kc from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/resolve-url.js";
@@ -1,4 +1,4 @@
1
- import o from "../_virtual/index3.js";
1
+ import o from "../_virtual/index2.js";
2
2
  const a = (r) => o(r).darken(0.2).isDark();
3
3
  export {
4
4
  a as isDarkColor
@@ -1,15 +1,51 @@
1
- import { toUint8Array as o } from "../shop-minis-react/node_modules/.pnpm/js-base64@3.7.7/node_modules/js-base64/base64.js";
2
- import { thumbHashToDataURL as a } from "../shop-minis-react/node_modules/.pnpm/thumbhash@0.1.1/node_modules/thumbhash/thumbhash.js";
3
- function n(r) {
4
- if (r)
1
+ import { toUint8Array as s } from "../shop-minis-react/node_modules/.pnpm/js-base64@3.7.7/node_modules/js-base64/base64.js";
2
+ import { thumbHashToDataURL as i } from "../shop-minis-react/node_modules/.pnpm/thumbhash@0.1.1/node_modules/thumbhash/thumbhash.js";
3
+ function U(t) {
4
+ if (t)
5
5
  try {
6
- const t = o(r);
7
- return a(t);
8
- } catch (t) {
9
- console.warn("Failed to decode thumbhash to data URL", t);
6
+ const e = s(t);
7
+ return i(e);
8
+ } catch (e) {
9
+ console.warn("Failed to decode thumbhash to data URL", e);
10
10
  }
11
11
  }
12
+ function f(t) {
13
+ return new Promise((e, n) => {
14
+ const r = new FileReader();
15
+ r.onloadend = () => e(r.result), r.onerror = n, r.readAsDataURL(t);
16
+ });
17
+ }
18
+ const o = {
19
+ xxsUrl: 32,
20
+ xsUrl: 64,
21
+ sUrl: 128,
22
+ xxsmUrl: 256,
23
+ xsmUrl: 384,
24
+ smUrl: 512,
25
+ mUrl: 640,
26
+ lUrl: 1080,
27
+ xlUrl: 2048
28
+ }, a = 0.05, c = Object.entries(o).sort(
29
+ ([, t], [, e]) => t - e
30
+ ), d = (t) => {
31
+ for (const [e, n] of c) {
32
+ const r = n + n * a;
33
+ if (t <= r) return e;
34
+ }
35
+ return "xlUrl";
36
+ }, m = (t, e) => {
37
+ const r = new RegExp(/\?+/g).test(t) ? "&" : "?";
38
+ return `${t}${r}width=${e}`;
39
+ }, u = (t) => {
40
+ if (!t) return "";
41
+ if (!t.startsWith("https://cdn.shopify.com"))
42
+ return t;
43
+ const e = window.innerWidth ?? screen.width, n = d(e);
44
+ return m(t, o[n]);
45
+ };
12
46
  export {
13
- n as getThumbhashDataURL
47
+ f as fileToDataUri,
48
+ u as getResizedImageUrl,
49
+ U as getThumbhashDataURL
14
50
  };
15
51
  //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sources":["../../src/utils/image.ts"],"sourcesContent":["import {toUint8Array} from 'js-base64'\nimport {thumbHashToDataURL} from 'thumbhash'\n\n/**\n * Converts a thumbhash string to a data URL for use as an image placeholder\n * @param thumbhash Base64 encoded thumbhash string\n * @returns Data URL that can be used as image source or undefined if conversion fails\n */\nexport function getThumbhashDataURL(thumbhash?: string): string | undefined {\n if (!thumbhash) return\n\n try {\n const thumbhashArray = toUint8Array(thumbhash)\n return thumbHashToDataURL(thumbhashArray)\n } catch (error) {\n console.warn('Failed to decode thumbhash to data URL', error)\n }\n}\n"],"names":["getThumbhashDataURL","thumbhash","thumbhashArray","toUint8Array","thumbHashToDataURL","error"],"mappings":";;AAQO,SAASA,EAAoBC,GAAwC;AAC1E,MAAKA;AAED,QAAA;AACI,YAAAC,IAAiBC,EAAaF,CAAS;AAC7C,aAAOG,EAAmBF,CAAc;AAAA,aACjCG,GAAO;AACN,cAAA,KAAK,0CAA0CA,CAAK;AAAA,IAAA;AAEhE;"}
1
+ {"version":3,"file":"image.js","sources":["../../src/utils/image.ts"],"sourcesContent":["import {toUint8Array} from 'js-base64'\nimport {thumbHashToDataURL} from 'thumbhash'\n\n/**\n * Converts a thumbhash string to a data URL for use as an image placeholder\n * @param thumbhash Base64 encoded thumbhash string\n * @returns Data URL that can be used as image source or undefined if conversion fails\n */\nexport function getThumbhashDataURL(thumbhash?: string): string | undefined {\n if (!thumbhash) return\n\n try {\n const thumbhashArray = toUint8Array(thumbhash)\n return thumbHashToDataURL(thumbhashArray)\n } catch (error) {\n console.warn('Failed to decode thumbhash to data URL', error)\n }\n}\n\n/** Converts a file to a data URI\n * @param file The file to convert\n * @returns A promise that resolves to the data URI string\n */\nexport function fileToDataUri(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onloadend = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nconst ImageSizes = {\n xxsUrl: 32,\n xsUrl: 64,\n sUrl: 128,\n xxsmUrl: 256,\n xsmUrl: 384,\n smUrl: 512,\n mUrl: 640,\n lUrl: 1080,\n xlUrl: 2048,\n} as const\n\ntype Key = keyof typeof ImageSizes\n\n/**\n * Acceptable offset for image sizes. An image could use the size that is within this offset.\n */\nconst offsetPercentage = 0.05\n\nconst sortedImageSizes = Object.entries(ImageSizes).sort(\n ([, firstSize], [, secondSize]) => firstSize - secondSize\n)\n\nconst getImageSizeKeyWithSize = (size: number): Key => {\n for (const [key, imgSize] of sortedImageSizes) {\n const upperBoundSize = imgSize + imgSize * offsetPercentage\n if (size <= upperBoundSize) return key as Key\n }\n\n return 'xlUrl'\n}\n\nconst resizeImage = (imageUrl: string, width: number) => {\n const pattern = new RegExp(/\\?+/g)\n const delimiter = pattern.test(imageUrl) ? '&' : '?'\n return `${imageUrl}${delimiter}width=${width}`\n}\n\n/**\n * Optimizes Shopify CDN image URLs by adding a width parameter based on screen size\n * @param url The image URL to optimize\n * @returns The optimized URL with width parameter if it's a Shopify CDN image, otherwise returns the original URL\n */\n\nexport const getResizedImageUrl = (url?: string): string => {\n if (!url) return ''\n\n // Only process Shopify CDN images\n if (!url.startsWith('https://cdn.shopify.com')) {\n return url\n }\n\n const width = window.innerWidth ?? screen.width\n\n const key = getImageSizeKeyWithSize(width)\n\n return resizeImage(url, ImageSizes[key])\n}\n"],"names":["getThumbhashDataURL","thumbhash","thumbhashArray","toUint8Array","thumbHashToDataURL","error","fileToDataUri","file","resolve","reject","reader","ImageSizes","offsetPercentage","sortedImageSizes","firstSize","secondSize","getImageSizeKeyWithSize","size","key","imgSize","upperBoundSize","resizeImage","imageUrl","width","delimiter","getResizedImageUrl","url"],"mappings":";;AAQO,SAASA,EAAoBC,GAAwC;AAC1E,MAAKA;AAED,QAAA;AACI,YAAAC,IAAiBC,EAAaF,CAAS;AAC7C,aAAOG,EAAmBF,CAAc;AAAA,aACjCG,GAAO;AACN,cAAA,KAAK,0CAA0CA,CAAK;AAAA,IAAA;AAEhE;AAMO,SAASC,EAAcC,GAA6B;AACzD,SAAO,IAAI,QAAQ,CAACC,GAASC,MAAW;AAChC,UAAAC,IAAS,IAAI,WAAW;AAC9B,IAAAA,EAAO,YAAY,MAAMF,EAAQE,EAAO,MAAgB,GACxDA,EAAO,UAAUD,GACjBC,EAAO,cAAcH,CAAI;AAAA,EAAA,CAC1B;AACH;AAEA,MAAMI,IAAa;AAAA,EACjB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT,GAOMC,IAAmB,MAEnBC,IAAmB,OAAO,QAAQF,CAAU,EAAE;AAAA,EAClD,CAAC,CAAG,EAAAG,CAAS,GAAG,GAAGC,CAAU,MAAMD,IAAYC;AACjD,GAEMC,IAA0B,CAACC,MAAsB;AACrD,aAAW,CAACC,GAAKC,CAAO,KAAKN,GAAkB;AACvC,UAAAO,IAAiBD,IAAUA,IAAUP;AACvC,QAAAK,KAAQG,EAAuB,QAAAF;AAAA,EAAA;AAG9B,SAAA;AACT,GAEMG,IAAc,CAACC,GAAkBC,MAAkB;AAEvD,QAAMC,IADU,IAAI,OAAO,MAAM,EACP,KAAKF,CAAQ,IAAI,MAAM;AACjD,SAAO,GAAGA,CAAQ,GAAGE,CAAS,SAASD,CAAK;AAC9C,GAQaE,IAAqB,CAACC,MAAyB;AACtD,MAAA,CAACA,EAAY,QAAA;AAGjB,MAAI,CAACA,EAAI,WAAW,yBAAyB;AACpC,WAAAA;AAGH,QAAAH,IAAQ,OAAO,cAAc,OAAO,OAEpCL,IAAMF,EAAwBO,CAAK;AAEzC,SAAOF,EAAYK,GAAKf,EAAWO,CAAG,CAAC;AACzC;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shopify/shop-minis-react",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "0.0.33",
4
+ "version": "0.0.34",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -38,7 +38,7 @@
38
38
  "typescript": ">=5.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@shopify/shop-minis-platform": "0.0.10",
41
+ "@shopify/shop-minis-platform": "0.0.11",
42
42
  "@tailwindcss/vite": "4.1.8",
43
43
  "@types/color": "3.0.6",
44
44
  "@types/lodash": "4.17.20",
@@ -1,22 +1,19 @@
1
+ /* eslint-disable jsx-a11y/alt-text */
1
2
  /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
2
3
  import {ImgHTMLAttributes, useCallback, useMemo, memo, useState} from 'react'
3
4
 
4
5
  import {cn} from '../../lib/utils'
5
- import {getThumbhashDataURL} from '../../utils/image'
6
+ import {getThumbhashDataURL, getResizedImageUrl} from '../../utils'
6
7
 
7
- type ThumbhashImageProps = ImgHTMLAttributes<HTMLImageElement> & {
8
- src: string
9
- thumbhash: string
10
- alt?: string | null
8
+ type ImageProps = ImgHTMLAttributes<HTMLImageElement> & {
9
+ src?: string
10
+ thumbhash?: string | null
11
11
  aspectRatio?: number | string
12
12
  }
13
13
 
14
- export const ThumbhashImage = memo(function ThumbhashImage(
15
- props: ThumbhashImageProps
16
- ) {
14
+ export const Image = memo(function Image(props: ImageProps) {
17
15
  const {
18
16
  src,
19
- alt,
20
17
  thumbhash,
21
18
  onLoad,
22
19
  className,
@@ -27,7 +24,7 @@ export const ThumbhashImage = memo(function ThumbhashImage(
27
24
 
28
25
  const [isLoaded, setIsLoaded] = useState(false)
29
26
 
30
- const dataURL = useMemo(
27
+ const thumbhashDataURL = useMemo(
31
28
  () => getThumbhashDataURL(thumbhash ?? undefined),
32
29
  [thumbhash]
33
30
  )
@@ -40,24 +37,27 @@ export const ThumbhashImage = memo(function ThumbhashImage(
40
37
  [onLoad]
41
38
  )
42
39
 
40
+ const resizedImageSrc = useMemo(() => getResizedImageUrl(src), [src])
41
+
43
42
  return (
44
43
  <div
45
44
  className={cn('relative w-full ', className)}
46
45
  style={{
47
46
  ...style,
48
47
  aspectRatio,
49
- backgroundImage: dataURL ? `url(${dataURL})` : undefined,
48
+ backgroundImage: thumbhashDataURL
49
+ ? `url(${thumbhashDataURL})`
50
+ : undefined,
50
51
  backgroundSize: 'cover',
51
52
  backgroundPosition: 'center',
52
53
  }}
53
54
  >
54
55
  <img
55
56
  className={cn(
56
- 'absolute inset-0 w-full h-full opacity-0 object-cover',
57
+ 'absolute inset-0 opacity-0 object-cover',
57
58
  isLoaded && 'opacity-100'
58
59
  )}
59
- src={src}
60
- alt={alt}
60
+ src={resizedImageSrc}
61
61
  onLoad={handleLoad}
62
62
  {...restProps}
63
63
  />
@@ -14,7 +14,7 @@ import {
14
14
  normalizeRating,
15
15
  } from '../../utils'
16
16
  import {isDarkColor} from '../../utils/colors'
17
- import {ThumbhashImage} from '../atoms/thumbhash-image'
17
+ import {Image} from '../atoms/image'
18
18
  import {Touchable} from '../atoms/touchable'
19
19
 
20
20
  interface MerchantCardContextValue {
@@ -100,7 +100,7 @@ function MerchantCardImage({
100
100
 
101
101
  if (thumbhash) {
102
102
  return (
103
- <ThumbhashImage
103
+ <Image
104
104
  data-slot="merchant-card-image"
105
105
  src={src}
106
106
  alt={alt}