var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { cA as remoteConfig, a as api, c as cn, _ as _sfc_main$e, W as useAssetsStore, k as useModelToNodeStore, h as assetService, s as st, u as useDialogStore, d as _export_sfc, l as useSubscription } from "./index-BqxAUePh.js";
import { b as reactive, E as computed, a as readonly, bq as defineComponent, cF as mergeModels, cG as useModel, j as createBlock, d as openBlock, br as unref, m as mergeProps, k as withCtx, e as createBaseVNode, G as normalizeStyle, c as createElementBlock, q as createCommentVNode, u as toDisplayString, p as renderSlot, cY as createSharedComposable, cp as useAsyncState, z as createVNode, w as watch, cX as useEventListener, ew as onWatcherCleanup, r as ref, A as createTextVNode, f as resolveComponent, o as onMounted, b9 as isRef, d8 as onBeforeUnmount, c6 as onUnmounted, s as normalizeClass } from "./vendor-other-DdEHkzYa.js";
import { i as script, y as script$1, e as script$2, a as script$3 } from "./vendor-primevue-DmDWdkDd.js";
import { u as useI18n } from "./vendor-vue-BIRc7P93.js";
function useFeatureFlags() {
  const flags = reactive({
    get supportsPreviewMetadata() {
      return api.getServerFeature(
        "supports_preview_metadata"
        /* SUPPORTS_PREVIEW_METADATA */
      );
    },
    get maxUploadSize() {
      return api.getServerFeature(
        "max_upload_size"
        /* MAX_UPLOAD_SIZE */
      );
    },
    get supportsManagerV4() {
      return api.getServerFeature(
        "extension.manager.supports_v4"
        /* MANAGER_SUPPORTS_V4 */
      );
    },
    get modelUploadButtonEnabled() {
      return remoteConfig.value.model_upload_button_enabled ?? api.getServerFeature(
        "model_upload_button_enabled",
        false
      );
    },
    get assetUpdateOptionsEnabled() {
      return remoteConfig.value.asset_update_options_enabled ?? api.getServerFeature(
        "asset_update_options_enabled",
        false
      );
    },
    get privateModelsEnabled() {
      return remoteConfig.value.private_models_enabled ?? api.getServerFeature("private_models_enabled", false);
    },
    get onboardingSurveyEnabled() {
      return remoteConfig.value.onboarding_survey_enabled ?? api.getServerFeature("onboarding_survey_enabled", true);
    },
    get huggingfaceModelImportEnabled() {
      return remoteConfig.value.huggingface_model_import_enabled ?? api.getServerFeature(
        "huggingface_model_import_enabled",
        false
      );
    }
  });
  const featureFlag = /* @__PURE__ */ __name((featurePath, defaultValue) => computed(() => api.getServerFeature(featurePath, defaultValue)), "featureFlag");
  return {
    flags: readonly(flags),
    featureFlag
  };
}
__name(useFeatureFlags, "useFeatureFlags");
const _hoisted_1$d = { class: "flex items-center gap-2 text-sm" };
const _hoisted_2$a = {
  key: 0,
  class: "text-base-foreground"
};
const _hoisted_3$7 = {
  key: 1,
  class: "text-base-foreground"
};
const _hoisted_4$5 = { class: "truncate" };
const _hoisted_5$5 = {
  key: 0,
  class: "icon-[lucide--check] text-base-foreground"
};
const _sfc_main$d = /* @__PURE__ */ defineComponent({
  ...{
    inheritAttrs: false
  },
  __name: "SingleSelect",
  props: /* @__PURE__ */ mergeModels({
    label: {},
    options: {},
    listMaxHeight: { default: "28rem" },
    popoverMinWidth: {},
    popoverMaxWidth: {}
  }, {
    "modelValue": { required: true },
    "modelModifiers": {}
  }),
  emits: ["update:modelValue"],
  setup(__props) {
    const selectedItem = useModel(__props, "modelValue");
    const { t } = useI18n();
    const getLabel = /* @__PURE__ */ __name((val) => {
      if (val == null) return __props.label ?? "";
      if (!__props.options) return __props.label ?? "";
      const found = __props.options.find((o) => o.value === val);
      return found ? found.name : __props.label ?? "";
    }, "getLabel");
    const optionStyle = computed(() => {
      if (!__props.popoverMinWidth && !__props.popoverMaxWidth) return void 0;
      const styles = [];
      if (__props.popoverMinWidth) styles.push(`min-width: ${__props.popoverMinWidth}`);
      if (__props.popoverMaxWidth) styles.push(`max-width: ${__props.popoverMaxWidth}`);
      return styles.join("; ");
    });
    return (_ctx, _cache) => {
      return openBlock(), createBlock(unref(script), mergeProps({
        modelValue: selectedItem.value,
        "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedItem.value = $event)
      }, _ctx.$attrs, {
        options: _ctx.options,
        "option-label": "name",
        "option-value": "value",
        unstyled: "",
        pt: {
          root: /* @__PURE__ */ __name(({ props }) => ({
            class: [
              // container
              "h-10 relative inline-flex cursor-pointer select-none items-center",
              // trigger surface
              "rounded-lg",
              "bg-secondary-background text-base-foreground",
              "border-[2.5px] border-solid border-transparent",
              "transition-all duration-200 ease-in-out",
              "focus-within:border-node-component-border",
              // disabled
              { "opacity-60 cursor-default": props.disabled }
            ]
          }), "root"),
          label: {
            class: (
              // Align with MultiSelect labelContainer spacing
              "flex-1 flex items-center whitespace-nowrap pl-4 py-2 outline-hidden"
            )
          },
          dropdown: {
            class: (
              // Right chevron touch area
              "flex shrink-0 items-center justify-center px-3 py-2"
            )
          },
          overlay: {
            class: unref(cn)(
              "mt-2 p-2 rounded-lg",
              "bg-base-background text-base-foreground",
              "border border-solid border-border-default"
            )
          },
          listContainer: /* @__PURE__ */ __name(() => ({
            style: `max-height: min(${_ctx.listMaxHeight}, 50vh)`,
            class: "scrollbar-custom"
          }), "listContainer"),
          list: {
            class: (
              // Same list tone/size as MultiSelect
              "flex flex-col gap-0 p-0 m-0 list-none border-none text-sm"
            )
          },
          option: /* @__PURE__ */ __name(({ context }) => ({
            class: unref(cn)(
              // Row layout
              "flex items-center justify-between gap-3 px-2 py-3 rounded",
              "hover:bg-secondary-background-hover",
              // Add focus state for keyboard navigation
              context.focused && "bg-secondary-background-hover",
              // Selected state + check icon
              context.selected && "bg-secondary-background-selected hover:bg-secondary-background-selected"
            )
          }), "option"),
          optionLabel: {
            class: "truncate"
          },
          optionGroupLabel: {
            class: "px-3 py-2 text-xs uppercase tracking-wide text-muted-foreground"
          },
          emptyMessage: {
            class: "px-3 py-2 text-sm text-muted-foreground"
          }
        },
        "aria-label": _ctx.label || unref(t)("g.singleSelectDropdown"),
        role: "combobox",
        "aria-expanded": false,
        "aria-haspopup": "listbox",
        tabindex: 0
      }), {
        value: withCtx((slotProps) => [
          createBaseVNode("div", _hoisted_1$d, [
            renderSlot(_ctx.$slots, "icon"),
            slotProps.value !== null && slotProps.value !== void 0 ? (openBlock(), createElementBlock("span", _hoisted_2$a, toDisplayString(getLabel(slotProps.value)), 1)) : (openBlock(), createElementBlock("span", _hoisted_3$7, toDisplayString(_ctx.label), 1))
          ])
        ]),
        dropdownicon: withCtx(() => _cache[1] || (_cache[1] = [
          createBaseVNode("i", { class: "icon-[lucide--chevron-down] text-muted-foreground" }, null, -1)
        ])),
        option: withCtx(({ option, selected }) => [
          createBaseVNode("div", {
            class: "flex w-full items-center justify-between gap-3",
            style: normalizeStyle(optionStyle.value)
          }, [
            createBaseVNode("span", _hoisted_4$5, toDisplayString(option.name), 1),
            selected ? (openBlock(), createElementBlock("i", _hoisted_5$5)) : createCommentVNode("", true)
          ], 4)
        ]),
        _: 3
      }, 16, ["modelValue", "options", "pt", "aria-label"]);
    };
  }
});
function formatDisplayName(folderName) {
  const specialCases = {
    loras: "LoRA",
    ipadapter: "IP-Adapter",
    sams: "SAM",
    clip_vision: "CLIP Vision",
    animatediff_motion_lora: "AnimateDiff Motion LoRA",
    animatediff_models: "AnimateDiff Model",
    vae: "VAE",
    sam2: "SAM 2",
    controlnet: "ControlNet",
    gligen: "GLIGEN"
  };
  if (specialCases[folderName]) {
    return specialCases[folderName];
  }
  return folderName.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
}
__name(formatDisplayName, "formatDisplayName");
const useModelTypes = createSharedComposable(() => {
  const {
    state: modelTypes,
    isLoading,
    error,
    execute: fetchModelTypes
  } = useAsyncState(
    async () => {
      const response = await api.getModelFolders();
      return response.map((folder) => ({
        name: formatDisplayName(folder.name),
        value: folder.name
      })).sort((a, b) => a.name.localeCompare(b.name));
    },
    [],
    {
      immediate: false,
      onError: /* @__PURE__ */ __name((err) => {
        console.error("Failed to fetch model types:", err);
      }, "onError")
    }
  );
  return {
    modelTypes,
    isLoading,
    error,
    fetchModelTypes
  };
});
const _hoisted_1$c = { class: "flex flex-col gap-4 text-sm text-muted-foreground" };
const _hoisted_2$9 = { class: "flex flex-col gap-2" };
const _hoisted_3$6 = { class: "m-0" };
const _hoisted_4$4 = { class: "flex items-center gap-3 bg-secondary-background p-3 rounded-lg" };
const _hoisted_5$4 = ["src", "alt"];
const _hoisted_6$3 = { class: "m-0 text-base-foreground" };
const _hoisted_7$3 = { class: "flex flex-col gap-2" };
const _hoisted_8$3 = { class: "" };
const _hoisted_9$3 = { class: "flex items-center gap-2" };
const _sfc_main$c = /* @__PURE__ */ defineComponent({
  __name: "UploadModelConfirmation",
  props: /* @__PURE__ */ mergeModels({
    metadata: {},
    previewImage: {}
  }, {
    "modelValue": {},
    "modelModifiers": {}
  }),
  emits: ["update:modelValue"],
  setup(__props) {
    const modelValue = useModel(__props, "modelValue");
    const { modelTypes, isLoading } = useModelTypes();
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$c, [
        createBaseVNode("div", _hoisted_2$9, [
          createBaseVNode("p", _hoisted_3$6, toDisplayString(_ctx.$t("assetBrowser.modelAssociatedWithLink")), 1),
          createBaseVNode("div", _hoisted_4$4, [
            _ctx.previewImage ? (openBlock(), createElementBlock("img", {
              key: 0,
              src: _ctx.previewImage,
              alt: _ctx.metadata?.filename || _ctx.metadata?.name || "Model preview",
              class: "w-14 h-14 rounded object-cover flex-shrink-0"
            }, null, 8, _hoisted_5$4)) : createCommentVNode("", true),
            createBaseVNode("p", _hoisted_6$3, toDisplayString(_ctx.metadata?.filename || _ctx.metadata?.name), 1)
          ])
        ]),
        createBaseVNode("div", _hoisted_7$3, [
          createBaseVNode("label", _hoisted_8$3, toDisplayString(_ctx.$t("assetBrowser.modelTypeSelectorLabel")), 1),
          createVNode(_sfc_main$d, {
            modelValue: modelValue.value,
            "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
            label: unref(isLoading) ? _ctx.$t("g.loading") : _ctx.$t("assetBrowser.modelTypeSelectorPlaceholder"),
            options: unref(modelTypes),
            disabled: unref(isLoading),
            "data-attr": "upload-model-step2-type-selector"
          }, null, 8, ["modelValue", "label", "options", "disabled"]),
          createBaseVNode("div", _hoisted_9$3, [
            _cache[1] || (_cache[1] = createBaseVNode("i", { class: "icon-[lucide--circle-question-mark]" }, null, -1)),
            createBaseVNode("span", null, toDisplayString(_ctx.$t("assetBrowser.notSureLeaveAsIs")), 1)
          ])
        ])
      ]);
    };
  }
});
const _hoisted_1$b = { class: "relative" };
const _hoisted_2$8 = ["aria-label", "src"];
const _sfc_main$b = /* @__PURE__ */ defineComponent({
  __name: "VideoHelpDialog",
  props: /* @__PURE__ */ mergeModels({
    videoUrl: {},
    ariaLabel: { default: "Help video" }
  }, {
    "modelValue": { type: Boolean, ...{ required: true } },
    "modelModifiers": {}
  }),
  emits: ["update:modelValue"],
  setup(__props) {
    const isVisible = useModel(__props, "modelValue");
    const handleEscapeKey = /* @__PURE__ */ __name((event) => {
      if (event.key === "Escape") {
        event.stopImmediatePropagation();
        event.stopPropagation();
        event.preventDefault();
        isVisible.value = false;
      }
    }, "handleEscapeKey");
    watch(
      isVisible,
      (visible) => {
        if (visible) {
          const stop = useEventListener(document, "keydown", handleEscapeKey, {
            capture: true
          });
          onWatcherCleanup(stop);
        }
      },
      { immediate: true }
    );
    return (_ctx, _cache) => {
      return openBlock(), createBlock(unref(script$1), {
        visible: isVisible.value,
        "onUpdate:visible": _cache[1] || (_cache[1] = ($event) => isVisible.value = $event),
        modal: "",
        closable: false,
        "close-on-escape": false,
        "dismissable-mask": true,
        pt: {
          root: { class: "video-help-dialog" },
          header: { class: "!hidden" },
          content: { class: "!p-0" },
          mask: { class: "!bg-black/70" }
        },
        style: { width: "90vw", maxWidth: "800px" }
      }, {
        default: withCtx(() => [
          createBaseVNode("div", _hoisted_1$b, [
            createVNode(_sfc_main$e, {
              variant: "textonly",
              size: "icon",
              class: "absolute top-4 right-6 z-10",
              "aria-label": _ctx.$t("g.close"),
              onClick: _cache[0] || (_cache[0] = ($event) => isVisible.value = false)
            }, {
              default: withCtx(() => _cache[2] || (_cache[2] = [
                createBaseVNode("i", { class: "pi pi-times text-sm" }, null, -1)
              ])),
              _: 1
            }, 8, ["aria-label"]),
            createBaseVNode("video", {
              autoplay: "",
              muted: "",
              loop: "",
              "aria-label": _ctx.ariaLabel,
              class: "w-full rounded-lg",
              src: _ctx.videoUrl
            }, toDisplayString(_ctx.$t("g.videoFailedToLoad")), 9, _hoisted_2$8)
          ])
        ]),
        _: 1
      }, 8, ["visible"]);
    };
  }
});
const _hoisted_1$a = { class: "flex justify-end gap-2 w-full" };
const _hoisted_2$7 = {
  key: 0,
  class: "mr-auto flex items-center gap-2"
};
const _hoisted_3$5 = { key: 4 };
const _hoisted_4$3 = {
  key: 0,
  class: "icon-[lucide--loader-circle] animate-spin"
};
const _hoisted_5$3 = {
  key: 0,
  class: "icon-[lucide--loader-circle] animate-spin"
};
const _sfc_main$a = /* @__PURE__ */ defineComponent({
  __name: "UploadModelFooter",
  props: {
    currentStep: {},
    isFetchingMetadata: { type: Boolean },
    isUploading: { type: Boolean },
    canFetchMetadata: { type: Boolean },
    canUploadModel: { type: Boolean },
    uploadStatus: {}
  },
  emits: ["back", "fetchMetadata", "upload", "close"],
  setup(__props, { emit: __emit }) {
    const { flags } = useFeatureFlags();
    const showCivitaiHelp = ref(false);
    const showHuggingFaceHelp = ref(false);
    const emit = __emit;
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$a, [
        _ctx.currentStep === 1 && unref(flags).huggingfaceModelImportEnabled ? (openBlock(), createElementBlock("div", _hoisted_2$7, [
          _cache[10] || (_cache[10] = createBaseVNode("i", { class: "icon-[lucide--circle-question-mark] text-muted-foreground" }, null, -1)),
          createVNode(_sfc_main$e, {
            variant: "muted-textonly",
            size: "sm",
            "data-attr": "upload-model-step1-help-civitai",
            onClick: _cache[0] || (_cache[0] = ($event) => showCivitaiHelp.value = true)
          }, {
            default: withCtx(() => [
              createTextVNode(toDisplayString(_ctx.$t("assetBrowser.providerCivitai")), 1)
            ]),
            _: 1
          }),
          createVNode(_sfc_main$e, {
            variant: "muted-textonly",
            size: "sm",
            "data-attr": "upload-model-step1-help-huggingface",
            onClick: _cache[1] || (_cache[1] = ($event) => showHuggingFaceHelp.value = true)
          }, {
            default: withCtx(() => [
              createTextVNode(toDisplayString(_ctx.$t("assetBrowser.providerHuggingFace")), 1)
            ]),
            _: 1
          })
        ])) : _ctx.currentStep === 1 ? (openBlock(), createBlock(_sfc_main$e, {
          key: 1,
          variant: "muted-textonly",
          size: "lg",
          class: "mr-auto underline",
          "data-attr": "upload-model-step1-help-link",
          onClick: _cache[2] || (_cache[2] = ($event) => showCivitaiHelp.value = true)
        }, {
          default: withCtx(() => [
            _cache[11] || (_cache[11] = createBaseVNode("i", { class: "icon-[lucide--circle-question-mark]" }, null, -1)),
            createBaseVNode("span", null, toDisplayString(_ctx.$t("assetBrowser.uploadModelHowDoIFindThis")), 1)
          ]),
          _: 1
        })) : createCommentVNode("", true),
        _ctx.currentStep === 1 ? (openBlock(), createBlock(_sfc_main$e, {
          key: 2,
          variant: "muted-textonly",
          size: "lg",
          "data-attr": "upload-model-step1-cancel-button",
          disabled: _ctx.isFetchingMetadata || _ctx.isUploading,
          onClick: _cache[3] || (_cache[3] = ($event) => emit("close"))
        }, {
          default: withCtx(() => [
            createTextVNode(toDisplayString(_ctx.$t("g.cancel")), 1)
          ]),
          _: 1
        }, 8, ["disabled"])) : createCommentVNode("", true),
        _ctx.currentStep !== 1 && _ctx.currentStep !== 3 ? (openBlock(), createBlock(_sfc_main$e, {
          key: 3,
          variant: "muted-textonly",
          size: "lg",
          "data-attr": `upload-model-step${_ctx.currentStep}-back-button`,
          disabled: _ctx.isFetchingMetadata || _ctx.isUploading,
          onClick: _cache[4] || (_cache[4] = ($event) => emit("back"))
        }, {
          default: withCtx(() => [
            createTextVNode(toDisplayString(_ctx.$t("g.back")), 1)
          ]),
          _: 1
        }, 8, ["data-attr", "disabled"])) : (openBlock(), createElementBlock("span", _hoisted_3$5)),
        _ctx.currentStep === 1 ? (openBlock(), createBlock(_sfc_main$e, {
          key: 5,
          variant: "secondary",
          size: "lg",
          "data-attr": "upload-model-step1-continue-button",
          disabled: !_ctx.canFetchMetadata || _ctx.isFetchingMetadata,
          onClick: _cache[5] || (_cache[5] = ($event) => emit("fetchMetadata"))
        }, {
          default: withCtx(() => [
            _ctx.isFetchingMetadata ? (openBlock(), createElementBlock("i", _hoisted_4$3)) : createCommentVNode("", true),
            createBaseVNode("span", null, toDisplayString(_ctx.$t("g.continue")), 1)
          ]),
          _: 1
        }, 8, ["disabled"])) : _ctx.currentStep === 2 ? (openBlock(), createBlock(_sfc_main$e, {
          key: 6,
          variant: "secondary",
          size: "lg",
          "data-attr": "upload-model-step2-confirm-button",
          disabled: !_ctx.canUploadModel || _ctx.isUploading,
          onClick: _cache[6] || (_cache[6] = ($event) => emit("upload"))
        }, {
          default: withCtx(() => [
            _ctx.isUploading ? (openBlock(), createElementBlock("i", _hoisted_5$3)) : createCommentVNode("", true),
            createBaseVNode("span", null, toDisplayString(_ctx.$t("assetBrowser.upload")), 1)
          ]),
          _: 1
        }, 8, ["disabled"])) : _ctx.currentStep === 3 && _ctx.uploadStatus === "success" ? (openBlock(), createBlock(_sfc_main$e, {
          key: 7,
          variant: "secondary",
          "data-attr": "upload-model-step3-finish-button",
          onClick: _cache[7] || (_cache[7] = ($event) => emit("close"))
        }, {
          default: withCtx(() => [
            createTextVNode(toDisplayString(_ctx.$t("assetBrowser.finish")), 1)
          ]),
          _: 1
        })) : createCommentVNode("", true),
        createVNode(_sfc_main$b, {
          modelValue: showCivitaiHelp.value,
          "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => showCivitaiHelp.value = $event),
          "video-url": "https://media.comfy.org/compressed_768/civitai_howto.webm",
          "aria-label": _ctx.$t("assetBrowser.uploadModelHelpVideo")
        }, null, 8, ["modelValue", "aria-label"]),
        createVNode(_sfc_main$b, {
          modelValue: showHuggingFaceHelp.value,
          "onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => showHuggingFaceHelp.value = $event),
          "video-url": "https://media.comfy.org/byom/huggingfacehowto.mp4",
          "aria-label": _ctx.$t("assetBrowser.uploadModelHelpVideo")
        }, null, 8, ["modelValue", "aria-label"])
      ]);
    };
  }
});
const _hoisted_1$9 = { class: "flex flex-1 flex-col gap-6 text-sm text-muted-foreground" };
const _hoisted_2$6 = {
  key: 0,
  class: "flex flex-1 flex-col items-center justify-center gap-2"
};
const _hoisted_3$4 = { class: "text-center" };
const _hoisted_4$2 = { class: "m-0 font-bold" };
const _hoisted_5$2 = {
  key: 1,
  class: "flex flex-col gap-2"
};
const _hoisted_6$2 = { class: "m-0 font-bold" };
const _hoisted_7$2 = { class: "m-0" };
const _hoisted_8$2 = { class: "flex flex-row items-center gap-3 p-4 bg-modal-card-background rounded-lg" };
const _hoisted_9$2 = ["src", "alt"];
const _hoisted_10$2 = { class: "flex flex-col justify-center items-start gap-1 flex-1" };
const _hoisted_11$1 = { class: "text-base-foreground m-0" };
const _hoisted_12$1 = { class: "text-sm text-muted m-0" };
const _hoisted_13$1 = {
  key: 2,
  class: "flex flex-1 flex-col items-center justify-center gap-6"
};
const _hoisted_14$1 = { class: "text-center" };
const _hoisted_15$1 = { class: "m-0 text-sm font-bold" };
const _hoisted_16$1 = {
  key: 0,
  class: "text-sm text-muted mb-0"
};
const _sfc_main$9 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelProgress",
  props: {
    status: {},
    error: {},
    metadata: {},
    modelType: {},
    previewImage: {}
  },
  setup(__props) {
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$9, [
        _ctx.status === "uploading" ? (openBlock(), createElementBlock("div", _hoisted_2$6, [
          _cache[0] || (_cache[0] = createBaseVNode("i", { class: "icon-[lucide--loader-circle] animate-spin text-6xl text-muted-foreground" }, null, -1)),
          createBaseVNode("div", _hoisted_3$4, [
            createBaseVNode("p", _hoisted_4$2, toDisplayString(_ctx.$t("assetBrowser.uploadingModel")), 1)
          ])
        ])) : _ctx.status === "success" ? (openBlock(), createElementBlock("div", _hoisted_5$2, [
          createBaseVNode("p", _hoisted_6$2, toDisplayString(_ctx.$t("assetBrowser.modelUploaded")), 1),
          createBaseVNode("p", _hoisted_7$2, toDisplayString(_ctx.$t("assetBrowser.findInLibrary", { type: _ctx.modelType })), 1),
          createBaseVNode("div", _hoisted_8$2, [
            _ctx.previewImage ? (openBlock(), createElementBlock("img", {
              key: 0,
              src: _ctx.previewImage,
              alt: _ctx.metadata?.filename || _ctx.metadata?.name || "Model preview",
              class: "w-14 h-14 rounded object-cover flex-shrink-0"
            }, null, 8, _hoisted_9$2)) : createCommentVNode("", true),
            createBaseVNode("div", _hoisted_10$2, [
              createBaseVNode("p", _hoisted_11$1, toDisplayString(_ctx.metadata?.filename || _ctx.metadata?.name), 1),
              createBaseVNode("p", _hoisted_12$1, toDisplayString(_ctx.modelType), 1)
            ])
          ])
        ])) : _ctx.status === "error" ? (openBlock(), createElementBlock("div", _hoisted_13$1, [
          _cache[1] || (_cache[1] = createBaseVNode("i", { class: "icon-[lucide--x-circle] text-6xl text-error" }, null, -1)),
          createBaseVNode("div", _hoisted_14$1, [
            createBaseVNode("p", _hoisted_15$1, toDisplayString(_ctx.$t("assetBrowser.uploadFailed")), 1),
            _ctx.error ? (openBlock(), createElementBlock("p", _hoisted_16$1, toDisplayString(_ctx.error), 1)) : createCommentVNode("", true)
          ])
        ])) : createCommentVNode("", true)
      ]);
    };
  }
});
const _hoisted_1$8 = { class: "flex flex-col justify-between h-full gap-6 text-sm" };
const _hoisted_2$5 = { class: "flex flex-col gap-6" };
const _hoisted_3$3 = { class: "flex flex-col gap-2" };
const _hoisted_4$1 = { class: "m-0 text-foreground" };
const _hoisted_5$1 = { class: "m-0" };
const _hoisted_6$1 = { class: "m-0 text-muted-foreground" };
const _hoisted_7$1 = { class: "inline-flex items-center gap-1 flex-wrap mt-2" };
const _hoisted_8$1 = { class: "inline-flex items-center gap-1" };
const _hoisted_9$1 = ["alt"];
const _hoisted_10$1 = { class: "inline-flex items-center gap-1" };
const _hoisted_11 = ["alt"];
const _hoisted_12 = { class: "flex flex-col gap-2" };
const _hoisted_13 = {
  key: 0,
  class: "text-xs text-error"
};
const _hoisted_14 = {
  key: 1,
  class: "text-foreground"
};
const _hoisted_15 = { class: "font-bold italic" };
const _hoisted_16 = { class: "text-sm text-muted" };
const civitaiIcon = "/assets/images/civitai.svg";
const civitaiUrl = "https://civitai.com/models";
const huggingFaceIcon = "/assets/images/hf-logo.svg";
const huggingFaceUrl = "https://huggingface.co";
const _sfc_main$8 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelUrlInput",
  props: {
    modelValue: {},
    error: {}
  },
  emits: ["update:modelValue"],
  setup(__props, { emit: __emit }) {
    const props = __props;
    const emit = __emit;
    const url = computed({
      get: /* @__PURE__ */ __name(() => props.modelValue, "get"),
      set: /* @__PURE__ */ __name((value) => emit("update:modelValue", value), "set")
    });
    return (_ctx, _cache) => {
      const _component_i18n_t = resolveComponent("i18n-t");
      return openBlock(), createElementBlock("div", _hoisted_1$8, [
        createBaseVNode("div", _hoisted_2$5, [
          createBaseVNode("div", _hoisted_3$3, [
            createBaseVNode("p", _hoisted_4$1, toDisplayString(_ctx.$t("assetBrowser.uploadModelDescription1Generic")), 1),
            createBaseVNode("div", _hoisted_5$1, [
              createBaseVNode("p", _hoisted_6$1, toDisplayString(_ctx.$t("assetBrowser.uploadModelDescription2Generic")), 1),
              createBaseVNode("span", _hoisted_7$1, [
                createBaseVNode("span", _hoisted_8$1, [
                  createBaseVNode("img", {
                    src: civitaiIcon,
                    alt: _ctx.$t("assetBrowser.providerCivitai"),
                    class: "w-4 h-4"
                  }, null, 8, _hoisted_9$1),
                  createBaseVNode("a", {
                    href: civitaiUrl,
                    target: "_blank",
                    rel: "noopener noreferrer",
                    class: "text-muted underline"
                  }, toDisplayString(_ctx.$t("assetBrowser.providerCivitai")), 1),
                  _cache[1] || (_cache[1] = createBaseVNode("span", null, ",", -1))
                ]),
                createBaseVNode("span", _hoisted_10$1, [
                  createBaseVNode("img", {
                    src: huggingFaceIcon,
                    alt: _ctx.$t("assetBrowser.providerHuggingFace"),
                    class: "w-4 h-4"
                  }, null, 8, _hoisted_11),
                  createBaseVNode("a", {
                    href: huggingFaceUrl,
                    target: "_blank",
                    rel: "noopener noreferrer",
                    class: "text-muted underline"
                  }, toDisplayString(_ctx.$t("assetBrowser.providerHuggingFace")), 1)
                ])
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_12, [
            createVNode(unref(script$2), {
              modelValue: url.value,
              "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => url.value = $event),
              autofocus: "",
              placeholder: _ctx.$t("assetBrowser.genericLinkPlaceholder"),
              class: "w-full bg-secondary-background border-0 p-4",
              "data-attr": "upload-model-step1-url-input"
            }, null, 8, ["modelValue", "placeholder"]),
            _ctx.error ? (openBlock(), createElementBlock("p", _hoisted_13, toDisplayString(_ctx.error), 1)) : (openBlock(), createElementBlock("p", _hoisted_14, [
              createVNode(_component_i18n_t, {
                keypath: "assetBrowser.maxFileSize",
                tag: "span"
              }, {
                size: withCtx(() => [
                  createBaseVNode("span", _hoisted_15, toDisplayString(_ctx.$t("assetBrowser.maxFileSizeValue")), 1)
                ]),
                _: 1
              })
            ]))
          ])
        ]),
        createBaseVNode("div", _hoisted_16, toDisplayString(_ctx.$t("assetBrowser.uploadModelHelpFooterText")), 1)
      ]);
    };
  }
});
const _hoisted_1$7 = { class: "flex flex-col gap-6 text-sm text-muted-foreground" };
const _hoisted_2$4 = { class: "flex flex-col gap-2" };
const _hoisted_3$2 = { class: "m-0" };
const _hoisted_4 = { class: "list-disc space-y-1 pl-5 mt-0" };
const _hoisted_5 = {
  href: "https://civitai.com/models",
  target: "_blank",
  class: "text-muted-foreground"
};
const _hoisted_6 = { class: "font-bold italic" };
const _hoisted_7 = { class: "flex flex-col gap-2" };
const _hoisted_8 = { class: "font-bold italic" };
const _hoisted_9 = {
  key: 0,
  class: "text-xs text-error"
};
const _hoisted_10 = {
  href: "https://civitai.com/models/10706/luisap-z-image-and-qwen-pixel-art-refiner?modelVersionId=2225295",
  target: "_blank",
  class: "text-muted-foreground"
};
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelUrlInputCivitai",
  props: /* @__PURE__ */ mergeModels({
    error: {}
  }, {
    "modelValue": { required: true },
    "modelModifiers": {}
  }),
  emits: ["update:modelValue"],
  setup(__props) {
    const url = useModel(__props, "modelValue");
    return (_ctx, _cache) => {
      const _component_i18n_t = resolveComponent("i18n-t");
      return openBlock(), createElementBlock("div", _hoisted_1$7, [
        createBaseVNode("div", _hoisted_2$4, [
          createBaseVNode("p", _hoisted_3$2, toDisplayString(_ctx.$t("assetBrowser.uploadModelDescription1")), 1),
          createBaseVNode("ul", _hoisted_4, [
            createBaseVNode("li", null, [
              createVNode(_component_i18n_t, {
                keypath: "assetBrowser.uploadModelDescription2",
                tag: "span"
              }, {
                link: withCtx(() => [
                  createBaseVNode("a", _hoisted_5, toDisplayString(_ctx.$t("assetBrowser.uploadModelDescription2Link")), 1)
                ]),
                _: 1
              })
            ]),
            createBaseVNode("li", null, [
              createVNode(_component_i18n_t, {
                keypath: "assetBrowser.uploadModelDescription3",
                tag: "span"
              }, {
                size: withCtx(() => [
                  createBaseVNode("span", _hoisted_6, toDisplayString(_ctx.$t("assetBrowser.maxFileSizeValue")), 1)
                ]),
                _: 1
              })
            ])
          ])
        ]),
        createBaseVNode("div", _hoisted_7, [
          createVNode(_component_i18n_t, {
            keypath: "assetBrowser.civitaiLinkLabel",
            tag: "label",
            class: "mb-0"
          }, {
            download: withCtx(() => [
              createBaseVNode("span", _hoisted_8, toDisplayString(_ctx.$t("assetBrowser.civitaiLinkLabelDownload")), 1)
            ]),
            _: 1
          }),
          createVNode(unref(script$2), {
            modelValue: url.value,
            "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => url.value = $event),
            autofocus: "",
            placeholder: _ctx.$t("assetBrowser.civitaiLinkPlaceholder"),
            class: "w-full bg-secondary-background border-0 p-4",
            "data-attr": "upload-model-step1-url-input"
          }, null, 8, ["modelValue", "placeholder"]),
          _ctx.error ? (openBlock(), createElementBlock("p", _hoisted_9, toDisplayString(_ctx.error), 1)) : (openBlock(), createBlock(_component_i18n_t, {
            key: 1,
            keypath: "assetBrowser.civitaiLinkExample",
            tag: "p",
            class: "text-xs"
          }, {
            example: withCtx(() => [
              createBaseVNode("strong", null, toDisplayString(_ctx.$t("assetBrowser.civitaiLinkExampleStrong")), 1)
            ]),
            link: withCtx(() => [
              createBaseVNode("a", _hoisted_10, toDisplayString(_ctx.$t("assetBrowser.civitaiLinkExampleUrl")), 1)
            ]),
            _: 1
          }))
        ])
      ]);
    };
  }
});
const civitaiImportSource = {
  type: "civitai",
  name: "Civitai",
  hostnames: ["civitai.com"]
};
const huggingfaceImportSource = {
  type: "huggingface",
  name: "Hugging Face",
  hostnames: ["huggingface.co"]
};
function validateSourceUrl(url, source) {
  try {
    const hostname = new URL(url).hostname.toLowerCase();
    return source.hostnames.some(
      (h) => hostname === h || hostname.endsWith(`.${h}`)
    );
  } catch {
    return false;
  }
}
__name(validateSourceUrl, "validateSourceUrl");
function useUploadModelWizard(modelTypes) {
  const { t } = useI18n();
  const assetsStore = useAssetsStore();
  const modelToNodeStore = useModelToNodeStore();
  const { flags } = useFeatureFlags();
  const currentStep = ref(1);
  const isFetchingMetadata = ref(false);
  const isUploading = ref(false);
  const uploadStatus = ref("idle");
  const uploadError = ref("");
  const wizardData = ref({
    url: "",
    name: "",
    tags: []
  });
  const selectedModelType = ref();
  const importSources = flags.huggingfaceModelImportEnabled ? [civitaiImportSource, huggingfaceImportSource] : [civitaiImportSource];
  const detectedSource = computed(() => {
    const url = wizardData.value.url.trim();
    if (!url) return null;
    return importSources.find((source) => validateSourceUrl(url, source)) ?? null;
  });
  watch(
    () => wizardData.value.url,
    () => {
      uploadError.value = "";
    }
  );
  const canFetchMetadata = computed(() => {
    return wizardData.value.url.trim().length > 0;
  });
  const canUploadModel = computed(() => {
    return !!selectedModelType.value;
  });
  async function fetchMetadata() {
    if (!canFetchMetadata.value) return;
    let cleanedUrl = wizardData.value.url.trim();
    try {
      cleanedUrl = new URL(encodeURI(cleanedUrl)).toString();
    } catch {
    }
    wizardData.value.url = cleanedUrl;
    const source = detectedSource.value;
    if (!source) {
      const supportedSources = importSources.map((s) => s.name).join(", ");
      uploadError.value = t("assetBrowser.unsupportedUrlSource", {
        sources: supportedSources
      });
      return;
    }
    isFetchingMetadata.value = true;
    try {
      const metadata = await assetService.getAssetMetadata(wizardData.value.url);
      if (metadata.filename) {
        try {
          metadata.filename = decodeURIComponent(metadata.filename);
        } catch {
        }
      }
      if (metadata.name) {
        try {
          metadata.name = decodeURIComponent(metadata.name);
        } catch {
        }
      }
      wizardData.value.metadata = metadata;
      wizardData.value.name = metadata.filename || metadata.name || "";
      wizardData.value.previewImage = metadata.preview_image;
      if (metadata.tags && metadata.tags.length > 0) {
        wizardData.value.tags = metadata.tags;
        const typeTag = metadata.tags.find(
          (tag) => modelTypes.value.some((type) => type.value === tag)
        );
        if (typeTag) {
          selectedModelType.value = typeTag;
        }
      }
      currentStep.value = 2;
    } catch (error) {
      console.error("Failed to retrieve metadata:", error);
      uploadError.value = error instanceof Error ? error.message : st(
        "assetBrowser.uploadModelFailedToRetrieveMetadata",
        "Failed to retrieve metadata. Please check the link and try again."
      );
      currentStep.value = 1;
    } finally {
      isFetchingMetadata.value = false;
    }
  }
  __name(fetchMetadata, "fetchMetadata");
  async function uploadModel() {
    if (!canUploadModel.value) return;
    const source = detectedSource.value;
    if (!source) {
      uploadError.value = t("assetBrowser.noValidSourceDetected");
      return false;
    }
    isUploading.value = true;
    uploadStatus.value = "uploading";
    try {
      const tags = selectedModelType.value ? ["models", selectedModelType.value] : ["models"];
      const filename = wizardData.value.metadata?.filename || wizardData.value.metadata?.name || "model";
      let previewId;
      if (wizardData.value.previewImage) {
        try {
          const baseFilename = filename.split(".")[0];
          let extension = "png";
          const mimeMatch = wizardData.value.previewImage.match(
            /^data:image\/([^;]+);/
          );
          if (mimeMatch) {
            extension = mimeMatch[1] === "jpeg" ? "jpg" : mimeMatch[1];
          }
          const previewAsset = await assetService.uploadAssetFromBase64({
            data: wizardData.value.previewImage,
            name: `${baseFilename}_preview.${extension}`,
            tags: ["preview"]
          });
          previewId = previewAsset.id;
        } catch (error) {
          console.error("Failed to upload preview image:", error);
        }
      }
      await assetService.uploadAssetFromUrl({
        url: wizardData.value.url,
        name: filename,
        tags,
        user_metadata: {
          source: source.type,
          source_url: wizardData.value.url,
          model_type: selectedModelType.value
        },
        preview_id: previewId
      });
      uploadStatus.value = "success";
      currentStep.value = 3;
      if (selectedModelType.value) {
        const providers = modelToNodeStore.getAllNodeProviders(
          selectedModelType.value
        );
        await Promise.all(
          providers.map(
            (provider) => assetsStore.updateModelsForNodeType(provider.nodeDef.name)
          )
        );
      }
      return true;
    } catch (error) {
      console.error("Failed to upload asset:", error);
      uploadStatus.value = "error";
      uploadError.value = error instanceof Error ? error.message : "Failed to upload model";
      currentStep.value = 3;
      return false;
    } finally {
      isUploading.value = false;
    }
  }
  __name(uploadModel, "uploadModel");
  function goToPreviousStep() {
    if (currentStep.value > 1) {
      currentStep.value = currentStep.value - 1;
    }
  }
  __name(goToPreviousStep, "goToPreviousStep");
  return {
    // State
    currentStep,
    isFetchingMetadata,
    isUploading,
    uploadStatus,
    uploadError,
    wizardData,
    selectedModelType,
    // Computed
    canFetchMetadata,
    canUploadModel,
    detectedSource,
    // Actions
    fetchMetadata,
    uploadModel,
    goToPreviousStep
  };
}
__name(useUploadModelWizard, "useUploadModelWizard");
const _hoisted_1$6 = { class: "upload-model-dialog flex flex-col justify-between gap-6 p-4 pt-6 border-t border-border-default" };
const _sfc_main$6 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelDialog",
  emits: ["upload-success"],
  setup(__props, { emit: __emit }) {
    const { flags } = useFeatureFlags();
    const dialogStore = useDialogStore();
    const { modelTypes, fetchModelTypes } = useModelTypes();
    const emit = __emit;
    const {
      currentStep,
      isFetchingMetadata,
      isUploading,
      uploadStatus,
      uploadError,
      wizardData,
      selectedModelType,
      canFetchMetadata,
      canUploadModel,
      fetchMetadata,
      uploadModel,
      goToPreviousStep
    } = useUploadModelWizard(modelTypes);
    async function handleFetchMetadata() {
      await fetchMetadata();
    }
    __name(handleFetchMetadata, "handleFetchMetadata");
    async function handleUploadModel() {
      const success = await uploadModel();
      if (success) {
        emit("upload-success");
      }
    }
    __name(handleUploadModel, "handleUploadModel");
    function handleClose() {
      dialogStore.closeDialog({ key: "upload-model" });
    }
    __name(handleClose, "handleClose");
    onMounted(() => {
      fetchModelTypes();
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$6, [
        unref(currentStep) === 1 && unref(flags).huggingfaceModelImportEnabled ? (openBlock(), createBlock(_sfc_main$8, {
          key: 0,
          modelValue: unref(wizardData).url,
          "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => unref(wizardData).url = $event),
          error: unref(uploadError),
          class: "flex-1"
        }, null, 8, ["modelValue", "error"])) : unref(currentStep) === 1 ? (openBlock(), createBlock(_sfc_main$7, {
          key: 1,
          modelValue: unref(wizardData).url,
          "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => unref(wizardData).url = $event),
          error: unref(uploadError)
        }, null, 8, ["modelValue", "error"])) : unref(currentStep) === 2 ? (openBlock(), createBlock(_sfc_main$c, {
          key: 2,
          modelValue: unref(selectedModelType),
          "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => isRef(selectedModelType) ? selectedModelType.value = $event : null),
          metadata: unref(wizardData).metadata,
          "preview-image": unref(wizardData).previewImage
        }, null, 8, ["modelValue", "metadata", "preview-image"])) : unref(currentStep) === 3 ? (openBlock(), createBlock(_sfc_main$9, {
          key: 3,
          status: unref(uploadStatus),
          error: unref(uploadError),
          metadata: unref(wizardData).metadata,
          "model-type": unref(selectedModelType),
          "preview-image": unref(wizardData).previewImage
        }, null, 8, ["status", "error", "metadata", "model-type", "preview-image"])) : createCommentVNode("", true),
        createVNode(_sfc_main$a, {
          "current-step": unref(currentStep),
          "is-fetching-metadata": unref(isFetchingMetadata),
          "is-uploading": unref(isUploading),
          "can-fetch-metadata": unref(canFetchMetadata),
          "can-upload-model": unref(canUploadModel),
          "upload-status": unref(uploadStatus),
          onBack: unref(goToPreviousStep),
          onFetchMetadata: handleFetchMetadata,
          onUpload: handleUploadModel,
          onClose: handleClose
        }, null, 8, ["current-step", "is-fetching-metadata", "is-uploading", "can-fetch-metadata", "can-upload-model", "upload-status", "onBack"])
      ]);
    };
  }
});
const UploadModelDialog = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-88e3f4c1"]]);
const _imports_0$1 = "" + new URL("images/civitai.svg", import.meta.url).href;
const _hoisted_1$5 = { class: "flex items-center gap-2 p-4 font-bold" };
const _hoisted_2$3 = {
  key: 0,
  src: _imports_0$1,
  class: "size-4"
};
const _hoisted_3$1 = { class: "rounded-full bg-white px-1.5 py-0 text-xxs font-inter font-semibold uppercase text-black" };
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelDialogHeader",
  setup(__props) {
    const { flags } = useFeatureFlags();
    const titleKey = computed(() => {
      return flags.huggingfaceModelImportEnabled ? "assetBrowser.uploadModelGeneric" : "assetBrowser.uploadModelFromCivitai";
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$5, [
        !unref(flags).huggingfaceModelImportEnabled ? (openBlock(), createElementBlock("img", _hoisted_2$3)) : createCommentVNode("", true),
        createBaseVNode("span", null, toDisplayString(_ctx.$t(titleKey.value)), 1),
        createBaseVNode("span", _hoisted_3$1, toDisplayString(_ctx.$t("g.beta")), 1)
      ]);
    };
  }
});
const _sfc_main$4 = {};
const _hoisted_1$4 = { class: "flex flex-1 flex-col items-center justify-center text-base text-muted-foreground" };
const _hoisted_2$2 = { class: "m-0 max-w-md" };
function _sfc_render$1(_ctx, _cache) {
  return openBlock(), createElementBlock("div", _hoisted_1$4, [
    createBaseVNode("p", _hoisted_2$2, toDisplayString(_ctx.$t("assetBrowser.upgradeFeatureDescription")), 1)
  ]);
}
__name(_sfc_render$1, "_sfc_render$1");
const UploadModelUpgradeModalBody = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$1]]);
const _hoisted_1$3 = { class: "flex flex-wrap justify-end gap-2 w-full" };
const _hoisted_2$1 = {
  href: "https://blog.comfy.org/p/comfy-cloud-new-features-and-pricing",
  target: "_blank",
  rel: "noopener noreferrer",
  class: "text-muted-foreground mr-auto underline flex items-center gap-2"
};
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelUpgradeModalFooter",
  emits: ["close", "subscribe"],
  setup(__props, { emit: __emit }) {
    const emit = __emit;
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$3, [
        createBaseVNode("a", _hoisted_2$1, [
          _cache[2] || (_cache[2] = createBaseVNode("i", { class: "icon-[lucide--external-link]" }, null, -1)),
          createBaseVNode("span", null, toDisplayString(_ctx.$t("g.learnMore")), 1)
        ]),
        createVNode(_sfc_main$e, {
          variant: "textonly",
          onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
        }, {
          default: withCtx(() => [
            createTextVNode(toDisplayString(_ctx.$t("g.close")), 1)
          ]),
          _: 1
        }),
        createVNode(_sfc_main$e, {
          variant: "secondary",
          onClick: _cache[1] || (_cache[1] = ($event) => emit("subscribe"))
        }, {
          default: withCtx(() => [
            createTextVNode(toDisplayString(_ctx.$t("subscription.required.subscribe")), 1)
          ]),
          _: 1
        })
      ]);
    };
  }
});
const _hoisted_1$2 = { class: "flex flex-col justify-between gap-10 p-4 border-t border-border-default w-auto max-w-[min(500px,90vw)]" };
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
  __name: "UploadModelUpgradeModal",
  setup(__props) {
    const dialogStore = useDialogStore();
    const { showSubscriptionDialog } = useSubscription();
    function handleClose() {
      dialogStore.closeDialog({ key: "upload-model-upgrade" });
    }
    __name(handleClose, "handleClose");
    function handleSubscribe() {
      showSubscriptionDialog();
    }
    __name(handleSubscribe, "handleSubscribe");
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", _hoisted_1$2, [
        createVNode(UploadModelUpgradeModalBody),
        createVNode(_sfc_main$3, {
          onClose: handleClose,
          onSubscribe: handleSubscribe
        })
      ]);
    };
  }
});
const _sfc_main$1 = {};
const _hoisted_1$1 = { class: "flex items-center gap-2 p-4 font-bold" };
function _sfc_render(_ctx, _cache) {
  return openBlock(), createElementBlock("div", _hoisted_1$1, [
    createBaseVNode("span", null, toDisplayString(_ctx.$t("assetBrowser.upgradeToUnlockFeature")), 1)
  ]);
}
__name(_sfc_render, "_sfc_render");
const UploadModelUpgradeModalHeader = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render]]);
function useModelUpload(execute) {
  const dialogStore = useDialogStore();
  const { flags } = useFeatureFlags();
  const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled);
  function showUploadDialog() {
    if (!flags.privateModelsEnabled) {
      dialogStore.showDialog({
        key: "upload-model-upgrade",
        headerComponent: UploadModelUpgradeModalHeader,
        component: _sfc_main$2,
        dialogComponentProps: {
          pt: {
            header: "py-0! pl-0!",
            content: "p-0!"
          }
        }
      });
    } else {
      dialogStore.showDialog({
        key: "upload-model",
        headerComponent: _sfc_main$5,
        component: UploadModelDialog,
        props: {
          onUploadSuccess: /* @__PURE__ */ __name(async () => {
            await execute?.();
          }, "onUploadSuccess")
        },
        dialogComponentProps: {
          pt: {
            header: "py-0! pl-0!",
            content: "p-0!"
          }
        }
      });
    }
  }
  __name(showUploadDialog, "showUploadDialog");
  return { isUploadButtonEnabled, showUploadDialog };
}
__name(useModelUpload, "useModelUpload");
const _imports_0 = "" + new URL("images/default-template.png", import.meta.url).href;
function useIntersectionObserver(target, callback, options = {}) {
  const { immediate = true, ...observerOptions } = options;
  const isSupported = typeof window !== "undefined" && "IntersectionObserver" in window;
  const isIntersecting = ref(false);
  let observer = null;
  const cleanup = /* @__PURE__ */ __name(() => {
    if (observer) {
      observer.disconnect();
      observer = null;
    }
  }, "cleanup");
  const observe = /* @__PURE__ */ __name(() => {
    cleanup();
    if (!isSupported || !target.value) return;
    observer = new IntersectionObserver((entries) => {
      isIntersecting.value = entries.some((entry) => entry.isIntersecting);
      callback(entries, observer);
    }, observerOptions);
    observer.observe(target.value);
  }, "observe");
  const unobserve = /* @__PURE__ */ __name(() => {
    if (observer && target.value) {
      observer.unobserve(target.value);
    }
  }, "unobserve");
  if (immediate) {
    watch(target, observe, { immediate: true, flush: "post" });
  }
  onBeforeUnmount(cleanup);
  return {
    isSupported,
    isIntersecting,
    observe,
    unobserve,
    cleanup
  };
}
__name(useIntersectionObserver, "useIntersectionObserver");
class MediaCacheService {
  static {
    __name(this, "MediaCacheService");
  }
  cache = reactive(/* @__PURE__ */ new Map());
  maxSize;
  maxAge;
  cleanupInterval = null;
  urlRefCount = /* @__PURE__ */ new Map();
  constructor(options = {}) {
    this.maxSize = options.maxSize ?? 100;
    this.maxAge = options.maxAge ?? 30 * 60 * 1e3;
    this.startCleanupInterval();
  }
  startCleanupInterval() {
    this.cleanupInterval = window.setInterval(
      () => {
        this.cleanup();
      },
      5 * 60 * 1e3
    );
  }
  cleanup() {
    const now = Date.now();
    const keysToDelete = [];
    for (const [key, entry] of Array.from(this.cache.entries())) {
      if (now - entry.lastAccessed > this.maxAge) {
        if (entry.objectUrl) {
          const refCount = this.urlRefCount.get(entry.objectUrl) || 0;
          if (refCount === 0) {
            URL.revokeObjectURL(entry.objectUrl);
            this.urlRefCount.delete(entry.objectUrl);
            keysToDelete.push(key);
          }
        } else {
          keysToDelete.push(key);
        }
      }
    }
    keysToDelete.forEach((key) => this.cache.delete(key));
    if (this.cache.size > this.maxSize) {
      const entries = Array.from(this.cache.entries());
      entries.sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);
      let removedCount = 0;
      const targetRemoveCount = this.cache.size - this.maxSize;
      for (const [key, entry] of entries) {
        if (removedCount >= targetRemoveCount) break;
        if (entry.objectUrl) {
          const refCount = this.urlRefCount.get(entry.objectUrl) || 0;
          if (refCount === 0) {
            URL.revokeObjectURL(entry.objectUrl);
            this.urlRefCount.delete(entry.objectUrl);
            this.cache.delete(key);
            removedCount++;
          }
        } else {
          this.cache.delete(key);
          removedCount++;
        }
      }
    }
  }
  async getCachedMedia(src) {
    let entry = this.cache.get(src);
    if (entry) {
      entry.lastAccessed = Date.now();
      return entry;
    }
    entry = {
      src,
      isLoading: true,
      lastAccessed: Date.now()
    };
    this.cache.set(src, entry);
    try {
      const response = await fetch(src, { cache: "force-cache" });
      if (!response.ok) {
        throw new Error(`Failed to fetch: ${response.status}`);
      }
      const blob = await response.blob();
      const objectUrl = URL.createObjectURL(blob);
      const updatedEntry = {
        src,
        blob,
        objectUrl,
        isLoading: false,
        lastAccessed: Date.now()
      };
      this.cache.set(src, updatedEntry);
      return updatedEntry;
    } catch (error) {
      console.warn("Failed to cache media:", src, error);
      const errorEntry = {
        src,
        error: true,
        isLoading: false,
        lastAccessed: Date.now()
      };
      this.cache.set(src, errorEntry);
      return errorEntry;
    }
  }
  acquireUrl(src) {
    const entry = this.cache.get(src);
    if (entry?.objectUrl) {
      const currentCount = this.urlRefCount.get(entry.objectUrl) || 0;
      this.urlRefCount.set(entry.objectUrl, currentCount + 1);
      return entry.objectUrl;
    }
    return void 0;
  }
  releaseUrl(src) {
    const entry = this.cache.get(src);
    if (entry?.objectUrl) {
      const count = (this.urlRefCount.get(entry.objectUrl) || 1) - 1;
      if (count <= 0) {
        URL.revokeObjectURL(entry.objectUrl);
        this.urlRefCount.delete(entry.objectUrl);
        this.cache.delete(src);
      } else {
        this.urlRefCount.set(entry.objectUrl, count);
      }
    }
  }
  clearCache() {
    for (const entry of Array.from(this.cache.values())) {
      if (entry.objectUrl) {
        URL.revokeObjectURL(entry.objectUrl);
      }
    }
    this.cache.clear();
    this.urlRefCount.clear();
  }
  destroy() {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval);
      this.cleanupInterval = null;
    }
    this.clearCache();
  }
}
let mediaCacheInstance = null;
function useMediaCache(options) {
  if (!mediaCacheInstance) {
    mediaCacheInstance = new MediaCacheService(options);
  }
  const getCachedMedia = /* @__PURE__ */ __name((src) => mediaCacheInstance.getCachedMedia(src), "getCachedMedia");
  const clearCache = /* @__PURE__ */ __name(() => mediaCacheInstance.clearCache(), "clearCache");
  const acquireUrl = /* @__PURE__ */ __name((src) => mediaCacheInstance.acquireUrl(src), "acquireUrl");
  const releaseUrl = /* @__PURE__ */ __name((src) => mediaCacheInstance.releaseUrl(src), "releaseUrl");
  return {
    getCachedMedia,
    clearCache,
    acquireUrl,
    releaseUrl,
    cache: mediaCacheInstance.cache
  };
}
__name(useMediaCache, "useMediaCache");
if (typeof window !== "undefined") {
  window.addEventListener("beforeunload", () => {
    if (mediaCacheInstance) {
      mediaCacheInstance.destroy();
    }
  });
}
const _hoisted_1 = ["src", "alt"];
const _hoisted_2 = {
  key: 2,
  class: "absolute inset-0 flex items-center justify-center"
};
const _hoisted_3 = ["alt"];
const _sfc_main = /* @__PURE__ */ defineComponent({
  __name: "LazyImage",
  props: {
    src: {},
    alt: { default: "" },
    containerClass: { type: [Array, Object, String, Number, null, Boolean], default: "" },
    imageClass: { type: [Array, Object, String, Number, null, Boolean], default: "" },
    imageStyle: {},
    rootMargin: { default: "300px" }
  },
  setup(__props) {
    const containerRef = ref(null);
    const isIntersecting = ref(false);
    const isImageLoaded = ref(false);
    const hasError = ref(false);
    const cachedSrc = ref(void 0);
    const { getCachedMedia, acquireUrl, releaseUrl } = useMediaCache();
    useIntersectionObserver(
      containerRef,
      (entries) => {
        const entry = entries[0];
        isIntersecting.value = entry?.isIntersecting ?? false;
      },
      {
        rootMargin: __props.rootMargin,
        threshold: 0.1
      }
    );
    const shouldLoad = computed(() => isIntersecting.value);
    watch(
      shouldLoad,
      async (shouldLoadVal) => {
        if (shouldLoadVal && __props.src && !cachedSrc.value && !hasError.value) {
          try {
            const cachedMedia = await getCachedMedia(__props.src);
            if (cachedMedia.error) {
              hasError.value = true;
            } else if (cachedMedia.objectUrl) {
              const acquiredUrl = acquireUrl(__props.src);
              cachedSrc.value = acquiredUrl || cachedMedia.objectUrl;
            } else {
              cachedSrc.value = __props.src;
            }
          } catch (error) {
            console.warn("Failed to load cached media:", error);
            cachedSrc.value = __props.src;
          }
        } else if (!shouldLoadVal) {
          if (cachedSrc.value?.startsWith("blob:")) {
            releaseUrl(__props.src);
          }
          isImageLoaded.value = false;
          cachedSrc.value = void 0;
          hasError.value = false;
        }
      },
      { immediate: true }
    );
    const onImageLoad = /* @__PURE__ */ __name(() => {
      isImageLoaded.value = true;
      hasError.value = false;
    }, "onImageLoad");
    const onImageError = /* @__PURE__ */ __name(() => {
      hasError.value = true;
      isImageLoaded.value = false;
    }, "onImageError");
    onUnmounted(() => {
      if (cachedSrc.value?.startsWith("blob:")) {
        releaseUrl(__props.src);
      }
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", {
        ref_key: "containerRef",
        ref: containerRef,
        class: normalizeClass(["relative flex h-full w-full items-center justify-center overflow-hidden", _ctx.containerClass])
      }, [
        !isImageLoaded.value ? (openBlock(), createBlock(unref(script$3), {
          key: 0,
          width: "100%",
          height: "100%",
          class: "absolute inset-0"
        })) : createCommentVNode("", true),
        cachedSrc.value ? (openBlock(), createElementBlock("img", {
          key: 1,
          src: cachedSrc.value,
          alt: _ctx.alt,
          draggable: "false",
          class: normalizeClass(_ctx.imageClass),
          style: normalizeStyle(_ctx.imageStyle),
          onLoad: onImageLoad,
          onError: onImageError
        }, null, 46, _hoisted_1)) : createCommentVNode("", true),
        hasError.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
          createBaseVNode("img", {
            src: _imports_0,
            alt: _ctx.alt,
            draggable: "false",
            class: normalizeClass(_ctx.imageClass),
            style: normalizeStyle(_ctx.imageStyle)
          }, null, 14, _hoisted_3)
        ])) : createCommentVNode("", true)
      ], 2);
    };
  }
});
export {
  _sfc_main$d as _,
  useModelUpload as a,
  _imports_0 as b,
  _sfc_main as c,
  useIntersectionObserver as d,
  useFeatureFlags as u
};
//# sourceMappingURL=LazyImage.vue_vue_type_script_setup_true_lang-q3mcFzhk.js.map
