@wewear/virtual-try-on 1.4.27 → 1.4.29

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/dist/index.d.ts CHANGED
@@ -3,10 +3,12 @@ export interface VirtualTryOnConfig {
3
3
  baseUrl: string;
4
4
  productPageSelector: string[];
5
5
  gallerySelector: string;
6
+ galleryElement?: HTMLElement;
6
7
  productImageSelector: string;
7
8
  buttonPosition: ButtonPosition;
8
9
  modelTier: string;
9
10
  outfitID: string;
11
+ singleton?: boolean;
10
12
  }
11
13
  export interface VirtualTryOnResult {
12
14
  imageUrl: string | null;
@@ -15,5 +17,5 @@ export interface VirtualTryOnResult {
15
17
  type: "error" | "warning";
16
18
  };
17
19
  }
18
- export { getWidgetInstance, initVirtualTryOn, } from "./installer.js";
20
+ export { getWidgetInstance, getWidgetInstances, initVirtualTryOn, } from "./installer.js";
19
21
  export { VirtualTryOnWidget } from "./widget.js";
package/dist/index.esm.js CHANGED
@@ -20,12 +20,6 @@ function getPositionStyles(position) {
20
20
  return "right: 20px; bottom: 20px;";
21
21
  }
22
22
  }
23
- function removeElements(selector) {
24
- const elements = document.querySelectorAll(selector);
25
- elements.forEach((element) => {
26
- element.remove();
27
- });
28
- }
29
23
 
30
24
  function createButtonContainer(buttonPosition, hasVirtualTryOn = false, onCameraClick, onRefreshClick, onToggleClick, isShowingVirtualTryOn = false) {
31
25
  const container = document.createElement("div");
@@ -395,7 +389,9 @@ class VirtualTryOnWidget {
395
389
  this.isShowingVirtualTryOn = false;
396
390
  this.lastModelImage = null;
397
391
  this.cameraButton = null;
392
+ this.modalElement = null;
398
393
  this.iframeMessageListener = null;
394
+ this.instanceId = ++VirtualTryOnWidget.instanceCounter;
399
395
  this.config = {
400
396
  baseUrl: config.baseUrl,
401
397
  productPageSelector: config.productPageSelector,
@@ -404,8 +400,18 @@ class VirtualTryOnWidget {
404
400
  buttonPosition: config.buttonPosition,
405
401
  modelTier: config.modelTier,
406
402
  outfitID: config.outfitID,
403
+ singleton: config.singleton,
404
+ galleryElement: config.galleryElement,
407
405
  };
408
406
  }
407
+ getContainer() {
408
+ const configContainer = this.config.galleryElement;
409
+ if (configContainer === null || configContainer === void 0 ? void 0 : configContainer.isConnected) {
410
+ return configContainer;
411
+ }
412
+ const queriedContainer = document.querySelector(this.config.gallerySelector);
413
+ return queriedContainer instanceof HTMLElement ? queriedContainer : null;
414
+ }
409
415
  async init() {
410
416
  try {
411
417
  const selectors = this.config.productPageSelector;
@@ -414,8 +420,8 @@ class VirtualTryOnWidget {
414
420
  if (!matches) {
415
421
  return;
416
422
  }
417
- const container = document.querySelector(this.config.gallerySelector);
418
- if (!container || !(container instanceof HTMLElement)) {
423
+ const container = this.getContainer();
424
+ if (!container) {
419
425
  console.warn("[WeWear VTO] Gallery container not found:", this.config.gallerySelector);
420
426
  return;
421
427
  }
@@ -462,7 +468,10 @@ class VirtualTryOnWidget {
462
468
  }
463
469
  }
464
470
  getAllProductImages() {
465
- const productImageElements = document.querySelectorAll(this.config.productImageSelector);
471
+ const container = this.getContainer();
472
+ const scopedElements = container === null || container === void 0 ? void 0 : container.querySelectorAll(this.config.productImageSelector);
473
+ const fallbackElements = document.querySelectorAll(this.config.productImageSelector);
474
+ const productImageElements = ((scopedElements === null || scopedElements === void 0 ? void 0 : scopedElements.length) ? scopedElements : fallbackElements);
466
475
  const images = [];
467
476
  productImageElements.forEach((img) => {
468
477
  const imageUrl = img.src ||
@@ -478,7 +487,8 @@ class VirtualTryOnWidget {
478
487
  return images;
479
488
  }
480
489
  showPhotoUploadModal(url) {
481
- removeElements(`.${CSS_CLASSES.MODAL}`);
490
+ this.closeModal();
491
+ VirtualTryOnWidget.activeWidgetId = this.instanceId;
482
492
  const modal = document.createElement("div");
483
493
  modal.className = CSS_CLASSES.MODAL;
484
494
  modal.style.cssText = `
@@ -572,7 +582,7 @@ class VirtualTryOnWidget {
572
582
  modal.style.opacity = "0";
573
583
  iframeContainer.style.transform = "scale(0.95)";
574
584
  iframeContainer.style.opacity = "0";
575
- setTimeout(() => modal.remove(), 300);
585
+ setTimeout(() => this.closeModal(), 300);
576
586
  };
577
587
  iframeContainer.appendChild(iframe);
578
588
  iframeContainer.appendChild(closeButton);
@@ -583,12 +593,25 @@ class VirtualTryOnWidget {
583
593
  closeButton.click();
584
594
  }
585
595
  };
596
+ this.modalElement = modal;
597
+ }
598
+ closeModal() {
599
+ if (this.modalElement) {
600
+ this.modalElement.remove();
601
+ this.modalElement = null;
602
+ }
603
+ if (VirtualTryOnWidget.activeWidgetId === this.instanceId) {
604
+ VirtualTryOnWidget.activeWidgetId = null;
605
+ }
586
606
  }
587
607
  setupIframeListener() {
588
608
  if (this.iframeMessageListener) {
589
609
  window.removeEventListener("message", this.iframeMessageListener);
590
610
  }
591
611
  this.iframeMessageListener = (event) => {
612
+ if (VirtualTryOnWidget.activeWidgetId !== this.instanceId) {
613
+ return;
614
+ }
592
615
  if (event.origin !== new URL(this.config.baseUrl).origin) {
593
616
  return;
594
617
  }
@@ -600,7 +623,7 @@ class VirtualTryOnWidget {
600
623
  }
601
624
  break;
602
625
  case "CLOSE_MODAL":
603
- removeElements(`.${CSS_CLASSES.MODAL}`);
626
+ this.closeModal();
604
627
  break;
605
628
  }
606
629
  };
@@ -625,8 +648,8 @@ class VirtualTryOnWidget {
625
648
  if (modal && modal instanceof HTMLElement) {
626
649
  modal.style.display = "none";
627
650
  }
628
- const container = document.querySelector(this.config.gallerySelector);
629
- if (container instanceof HTMLElement) {
651
+ const container = this.getContainer();
652
+ if (container) {
630
653
  showProductLoading(container, "Preparing your personalized look");
631
654
  }
632
655
  try {
@@ -671,17 +694,62 @@ class VirtualTryOnWidget {
671
694
  removeStatusBadge(container);
672
695
  this.showButtonContainer(container);
673
696
  }
674
- removeElements(`.${CSS_CLASSES.MODAL}`);
697
+ this.closeModal();
698
+ }
699
+ }
700
+ async fetchVtoData(outfitId, userBrand) {
701
+ try {
702
+ const vtoDataUrl = new URL("/api/vto/data", this.config.baseUrl);
703
+ vtoDataUrl.searchParams.set("outfit_id", outfitId);
704
+ vtoDataUrl.searchParams.set("user_brand", userBrand);
705
+ const response = await fetch(vtoDataUrl.toString());
706
+ if (response.status === 404)
707
+ return null;
708
+ if (!response.ok) {
709
+ const errorText = await response.text();
710
+ console.warn(`[WeWear VTO] Failed to retrieve VTO data (${response.status}): ${errorText}`);
711
+ return null;
712
+ }
713
+ return (await response.json());
714
+ }
715
+ catch (error) {
716
+ console.warn("[WeWear VTO] Error retrieving VTO data:", error);
717
+ return null;
675
718
  }
676
719
  }
677
720
  async callVtoApi(modelImage, productImages) {
678
721
  var _a;
722
+ const outfitID = (_a = this.config.outfitID) === null || _a === void 0 ? void 0 : _a.trim();
723
+ let resolvedProductImages = productImages;
724
+ let resolvedGeminiModel = null;
725
+ let resolvedAdditionalPrompt = null;
726
+ if (outfitID) {
727
+ const userBrand = window.location.href.split("#")[0];
728
+ const vtoData = await this.fetchVtoData(outfitID, userBrand);
729
+ if (vtoData) {
730
+ if (Array.isArray(vtoData.images) && vtoData.images.length > 0) {
731
+ resolvedProductImages = vtoData.images;
732
+ }
733
+ if (typeof vtoData.gemini_model === "string" && vtoData.gemini_model) {
734
+ resolvedGeminiModel = vtoData.gemini_model;
735
+ }
736
+ if (typeof vtoData.additional_prompt === "string" &&
737
+ vtoData.additional_prompt) {
738
+ resolvedAdditionalPrompt = vtoData.additional_prompt;
739
+ }
740
+ console.log(`[WeWear VTO] Using stored VTO data for outfit_id=${outfitID}, user_brand=${userBrand}`);
741
+ }
742
+ }
679
743
  const formData = new FormData();
680
744
  formData.append("model_image", modelImage, "model_image.png");
681
- formData.append("product_image_urls", JSON.stringify(productImages));
745
+ formData.append("product_image_urls", JSON.stringify(resolvedProductImages));
682
746
  formData.append("page_url", window.location.href);
683
747
  formData.append("model_tier", this.config.modelTier);
684
- const outfitID = (_a = this.config.outfitID) === null || _a === void 0 ? void 0 : _a.trim();
748
+ if (resolvedGeminiModel)
749
+ formData.append("gemini_model", resolvedGeminiModel);
750
+ if (resolvedAdditionalPrompt) {
751
+ formData.append("additional_prompt", resolvedAdditionalPrompt);
752
+ }
685
753
  if (outfitID)
686
754
  formData.append("outfit_id", outfitID);
687
755
  const res = await fetch(`${this.config.baseUrl}/api/vto`, {
@@ -717,8 +785,8 @@ class VirtualTryOnWidget {
717
785
  replaceProductImage(imageUrl) {
718
786
  try {
719
787
  this.virtualTryOnImageUrl = imageUrl;
720
- const container = document.querySelector(this.config.gallerySelector);
721
- if (!container || !(container instanceof HTMLElement)) {
788
+ const container = this.getContainer();
789
+ if (!container) {
722
790
  console.warn("[WeWear VTO] Gallery container not found for image replacement:", this.config.gallerySelector);
723
791
  return;
724
792
  }
@@ -843,8 +911,14 @@ class VirtualTryOnWidget {
843
911
  }
844
912
  destroy() {
845
913
  try {
846
- removeElements(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
847
- removeElements(`.${CSS_CLASSES.MODAL}`);
914
+ const container = this.getContainer();
915
+ if (container) {
916
+ const existingButtons = container.querySelectorAll(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
917
+ existingButtons.forEach((btn) => {
918
+ btn.remove();
919
+ });
920
+ }
921
+ this.closeModal();
848
922
  this.cameraButton = null;
849
923
  if (this.iframeMessageListener) {
850
924
  window.removeEventListener("message", this.iframeMessageListener);
@@ -862,42 +936,63 @@ class VirtualTryOnWidget {
862
936
  }
863
937
  }
864
938
  }
939
+ VirtualTryOnWidget.instanceCounter = 0;
940
+ VirtualTryOnWidget.activeWidgetId = null;
865
941
 
866
- let widgetInstance = null;
942
+ let widgetInstances = [];
867
943
  function initVirtualTryOn(config) {
868
944
  try {
869
- if (widgetInstance) {
870
- widgetInstance.destroy();
871
- widgetInstance = null;
872
- }
945
+ widgetInstances.forEach((instance) => {
946
+ instance.destroy();
947
+ });
948
+ widgetInstances = [];
873
949
  if (!config) {
874
950
  console.log("[WeWear VTO] Missing configuration. Widget not initialized.");
875
951
  return;
876
952
  }
877
- widgetInstance = new VirtualTryOnWidget(config);
878
- if (document.readyState === "loading") {
879
- document.addEventListener("DOMContentLoaded", () => {
880
- widgetInstance === null || widgetInstance === void 0 ? void 0 : widgetInstance.init().catch((error) => {
881
- console.error("[WeWear VTO] Failed to initialize after DOM ready:", error);
882
- });
953
+ const allContainers = Array.from(document.querySelectorAll(config.gallerySelector)).filter((container) => container instanceof HTMLElement);
954
+ const shouldUseSingleton = config.singleton !== false;
955
+ if (allContainers.length > 1 && !shouldUseSingleton) {
956
+ widgetInstances = allContainers.map((galleryElement) => {
957
+ return new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement }));
883
958
  });
884
959
  }
885
960
  else {
886
- widgetInstance.init().catch((error) => {
887
- console.error("[WeWear VTO] Failed to initialize:", error);
961
+ const firstContainer = allContainers[0];
962
+ widgetInstances = [
963
+ new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement: firstContainer })),
964
+ ];
965
+ }
966
+ const initializeWidgets = () => {
967
+ widgetInstances.forEach((widget) => {
968
+ widget.init().catch((error) => {
969
+ console.error("[WeWear VTO] Failed to initialize:", error);
970
+ });
971
+ });
972
+ };
973
+ if (document.readyState === "loading") {
974
+ document.addEventListener("DOMContentLoaded", initializeWidgets, {
975
+ once: true,
888
976
  });
889
977
  }
978
+ else {
979
+ initializeWidgets();
980
+ }
890
981
  }
891
982
  catch (error) {
892
983
  console.error("[WeWear VTO] Initialization error:", error);
893
984
  }
894
985
  }
895
986
  function getWidgetInstance() {
896
- return widgetInstance;
987
+ var _a;
988
+ return (_a = widgetInstances[0]) !== null && _a !== void 0 ? _a : null;
989
+ }
990
+ function getWidgetInstances() {
991
+ return [...widgetInstances];
897
992
  }
898
993
  if (typeof window !== "undefined") {
899
994
  initVirtualTryOn();
900
995
  }
901
996
 
902
- export { VirtualTryOnWidget, getWidgetInstance, initVirtualTryOn };
997
+ export { VirtualTryOnWidget, getWidgetInstance, getWidgetInstances, initVirtualTryOn };
903
998
  //# sourceMappingURL=index.esm.js.map