diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c4670fc..1a34a9404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,29 @@ # Change Log for SD.Next -## Update for 07/21/2023 +## Update for 07/22/2023 -- new loading screens and artwork -- extra networks: add add/remove tags to prompt (e.g. lora activation keywords) -- extensions: fix couple of compatibility items -- number of hires fixes -- diffusers: option to set vae upcast in settings - - sd-xl: enable fp16 vae decode when using optimized vae +- general: + - new loading screens and artwork + - extra networks: add add/remove tags to prompt (e.g. lora activation keywords) + - extensions: fix couple of compatibility items + - firefox compatibility improvements +- original + - fix hires secondary sampler + this now fully obsoletes `fallback_sampler` and `force_latent_sampler` +- diffusers: + - implement img2img and inpainting (experimental) + actual support and qualiy depends on model + it works as expected for sd 1.5, but not so much for sd-xl for now + - add option to save image before refiner pass + - option to set vae upcast in settings + - enable fp16 vae decode when using optimized vae this pretty much doubles performance of decode step (delay after generate is done) - - sd-xl: loading vae now applies to both base and refiner - -- diffusers 0.19.dev + - sd-xl: loading vae now applies to both base and refiner and saves a bit of vram +- diffusers: future-proof + requires `diffusers==0.19.dev`, not yet released, but can be installed manually - sd-xl: denoising_start/denoising_end - sd-xl: enable dual prompts - this is used regardless if refiner is enabled/loaded + dual prompt is used if set regardless if refiner is enabled/loaded if refiner is loaded & enabled, refiner prompt will also be used for refiner pass - primary prompt goes to [OpenAI CLIP-ViT/L-14](https://huggingface.co/openai/clip-vit-large-patch14) - refiner prompt goes to [OpenCLIP-ViT/bigG-14](https://huggingface.co/laion/CLIP-ViT-bigG-14-laion2B-39B-b160k) diff --git a/TODO.md b/TODO.md index 7a7294d13..d7eecb817 100644 --- a/TODO.md +++ b/TODO.md @@ -13,13 +13,11 @@ Stuff to be added, in no particular order... - Diffusers: - Add SD-XL Lora - - Add SD-XL Sketch/Inpaint + - Fix SD-XL Img2img/Inpaint - Add VAE direct load from safetensors - Fix Kandinsky 2.2 model - Fix DeepFloyd IF model - - Redo Prompt parser - - Add Explicit VAE step - - Add Save image before refiner (depends on explicit VAE) + - Redo Prompt parser for diffusers - Add unCLIP model - Technical debt: - Port **A1111** stuff diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 098f6cd88..8063252cf 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 098f6cd887ac5f6f5f0e7cc9d81460095d2be012 +Subproject commit 8063252cf5c1ddac5c91135e448b9aea7cf3c871 diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 66827337d..6386e0c8f 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 66827337d7688b4255b9abb25edcc21b4f3ce913 +Subproject commit 6386e0c8f5b5ca0b6f81836edc85f83d4a1d6781 diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js index 85f30042a..cec604279 100644 --- a/javascript/imageViewer.js +++ b/javascript/imageViewer.js @@ -54,6 +54,8 @@ function modalKeyHandler(event) { } function showModal(event) { + // console.log('showModal', event); + // const source = gradioApp().querySelectorAll('.gradio-gallery > div > img')[0]; const source = event.target || event.srcElement; const modalImage = gradioApp().getElementById('modalImage'); const lb = gradioApp().getElementById('lightboxModal'); @@ -85,14 +87,17 @@ function modalZoomSet(modalImage, enable) { function setupImageForLightbox(e) { if (e.dataset.modded) return; + console.log('setupImageForLightbox', e); e.dataset.modded = true; e.style.cursor = 'pointer'; e.style.userSelect = 'none'; - e.addEventListener('click', (evt) => { + const event = (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) ? 'mousedown' : 'click'; // silly firefox workaround since it triggers events in wrong order + e.addEventListener(event, (evt) => { if (evt.button !== 0) return; const initialZoom = (localStorage.getItem('modalZoom') || true) === 'yes'; modalZoomSet(gradioApp().getElementById('modalImage'), initialZoom); evt.preventDefault(); + evt.stopPropagation(); showModal(evt); }, true); } diff --git a/javascript/setHints.js b/javascript/setHints.js index b78efa31e..7b572a425 100644 --- a/javascript/setHints.js +++ b/javascript/setHints.js @@ -1,4 +1,4 @@ -const locale = { +const localeData = { data: [], timeout: null, finished: false, @@ -7,25 +7,25 @@ const locale = { }; function tooltipCreate() { - locale.el = document.createElement('div'); - locale.el.className = 'tooltip'; - locale.el.id = 'tooltip-container'; - locale.el.innerText = 'this is a hint'; - gradioApp().appendChild(locale.el); - if (window.opts.tooltips === 'None') locale.type = 0; - if (window.opts.tooltips === 'Browser default') locale.type = 1; - if (window.opts.tooltips === 'UI tooltips') locale.type = 2; + localeData.el = document.createElement('div'); + localeData.el.className = 'tooltip'; + localeData.el.id = 'tooltip-container'; + localeData.el.innerText = 'this is a hint'; + gradioApp().appendChild(localeData.el); + if (window.opts.tooltips === 'None') localeData.type = 0; + if (window.opts.tooltips === 'Browser default') localeData.type = 1; + if (window.opts.tooltips === 'UI tooltips') localeData.type = 2; } async function tooltipShow(e) { if (e.target.dataset.hint) { - locale.el.classList.add('tooltip-show'); - locale.el.innerHTML = `${e.target.textContent}
${e.target.dataset.hint}`; + localeData.el.classList.add('tooltip-show'); + localeData.el.innerHTML = `${e.target.textContent}
${e.target.dataset.hint}`; } } async function tooltipHide(e) { - locale.el.classList.remove('tooltip-show'); + localeData.el.classList.remove('tooltip-show'); } async function validateHints(elements, data) { @@ -47,11 +47,11 @@ async function validateHints(elements, data) { } async function setHints() { - if (locale.finished) return; - if (locale.data.length === 0) { + if (localeData.finished) return; + if (localeData.data.length === 0) { const res = await fetch('/file=html/locale_en.json'); const json = await res.json(); - locale.data = Object.values(json).flat(); + localeData.data = Object.values(json).flat(); } const elements = [ ...Array.from(gradioApp().querySelectorAll('button')), @@ -59,22 +59,22 @@ async function setHints() { ]; if (elements.length === 0) return; if (Object.keys(opts).length === 0) return; - if (!locale.el) tooltipCreate(); + if (!localeData.el) tooltipCreate(); let localized = 0; let hints = 0; - locale.finished = true; + localeData.finished = true; const t0 = performance.now(); for (const el of elements) { - const found = locale.data.find((l) => l.label === el.textContent.trim()); + const found = localeData.data.find((l) => l.label === el.textContent.trim()); if (found?.localized?.length > 0) { localized++; el.textContent = found.localized; } if (found?.hint?.length > 0) { hints++; - if (locale.type === 1) { + if (localeData.type === 1) { el.title = found.hint; - } else if (locale.type === 2) { + } else if (localeData.type === 2) { el.dataset.hint = found.hint; el.addEventListener('mouseover', tooltipShow); el.addEventListener('mouseout', tooltipHide); @@ -84,12 +84,12 @@ async function setHints() { } } const t1 = performance.now(); - console.log('setHints', { type: locale.type, elements: elements.length, localized, hints, data: locale.data.length, time: t1 - t0 }); + console.log('setHints', { type: localeData.type, elements: elements.length, localized, hints, data: localeData.data.length, time: t1 - t0 }); removeSplash(); - // validateHints(elements, locale.data) + // validateHints(elements, localeData.data) } onAfterUiUpdate(async () => { - if (locale.timeout) clearTimeout(locale.timeout); - locale.timeout = setTimeout(setHints, 250); + if (localeData.timeout) clearTimeout(localeData.timeout); + localeData.timeout = setTimeout(setHints, 250); }); diff --git a/javascript/style.css b/javascript/style.css index 93386ee1e..e1e8c1747 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -355,7 +355,7 @@ div#extras_scale_to_tab div.form{ flex-direction: row; } -.modalControls { display: flex; justify-content: space-evenly; background-color: transparent; position: absolute; width: -webkit-fill-available; z-index: 1; } +.modalControls { display: flex; justify-content: space-evenly; background-color: transparent; position: absolute; width: 99%; z-index: 1; } .modalControls:hover { background-color: #50505050; } .modalControls span { color: white; font-size: 2em; font-weight: bold; cursor: pointer; filter: grayscale(100%); } .modalControls span:hover, .modalControls span:focus { color: var(--highlight-color); filter: none; } diff --git a/modules/processing.py b/modules/processing.py index 82cf63415..e820a967e 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -221,12 +221,16 @@ class StableDiffusionProcessing: image_conditioning = image_conditioning.to(device=shared.device, dtype=source_image.dtype) return image_conditioning + def diffusers_image_conditioning(self, _source_image, latent_image, _image_mask=None): + # shared.log.warning('Diffusers not implemented: img2img_image_conditioning') + return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1) + def img2img_image_conditioning(self, source_image, latent_image, image_mask=None): source_image = devices.cond_cast_float(source_image) # HACK: Using introspection as the Depth2Image model doesn't appear to uniquely # identify itself with a field common to all models. The conditioning_key is also hybrid. if shared.backend == shared.Backend.DIFFUSERS: - shared.log.warning('Diffusers not implemented: img2img_image_conditioning') + return self.diffusers_image_conditioning(source_image, latent_image, image_mask) if isinstance(self.sd_model, LatentDepth2ImageDiffusion): return self.depth2img_image_conditioning(source_image) if hasattr(self.sd_model, 'cond_stage_key') and self.sd_model.cond_stage_key == "edit": @@ -992,20 +996,19 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.refiner_negative = refiner_negative self.enable_hr = None - def init(self, all_prompts, all_seeds, all_subseeds): - image_mask = self.image_mask - if shared.backend == shared.Backend.DIFFUSERS and image_mask is None: + if shared.backend == shared.Backend.DIFFUSERS and self.image_mask is None: sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.IMAGE_2_IMAGE) - elif shared.backend == shared.Backend.DIFFUSERS and image_mask is not None: + elif shared.backend == shared.Backend.DIFFUSERS and self.image_mask is not None: sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.INPAINTING) self.sd_model.dtype = self.sd_model.unet.dtype - force_latent_upscaler = shared.opts.data.get('force_latent_sampler') - if self.sampler_name in ['PLMS']: - self.sampler_name = force_latent_upscaler if force_latent_upscaler != 'None' else shared.opts.fallback_sampler # PLMS does not support img2img, use fallback instead + if self.sampler_name == "PLMS": + self.sampler_name = 'UniPC' self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) + crop_region = None + image_mask = self.image_mask if image_mask is not None: image_mask = image_mask.convert('L') if self.inpainting_mask_invert: @@ -1043,11 +1046,13 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): if image_mask is not None: image_masked = Image.new('RGBa', (image.width, image.height)) image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L'))) + self.mask = image_mask # assign early for diffusers self.overlay_images.append(image_masked.convert('RGBA')) # crop_region is not None if we are doing inpaint full res if crop_region is not None: image = image.crop(crop_region) image = images.resize_image(2, image, self.width, self.height) + self.init_images = image # assign early for diffusers if image_mask is not None: if self.inpainting_fill != 1: image = masking.fill(image, latent_mask) @@ -1067,16 +1072,14 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): batch_images = np.array(imgs) else: raise RuntimeError(f"bad number of images passed: {len(imgs)}; expecting {self.batch_size} or less") + if shared.backend == shared.Backend.DIFFUSERS: + # we've already set self.init_images and self.mask and we dont need any more processing + return + image = torch.from_numpy(batch_images) image = 2. * image - 1. image = image.to(device=shared.device, dtype=devices.dtype_vae) - - if shared.backend == shared.Backend.ORIGINAL: - self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) - else: - # TODO Diffusers don't pre-encode the latents for diffusers to allow the UI to stay general for different model types - self.init_latent = torch.Tensor(1) - + self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) if self.resize_mode == 3: self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") if image_mask is not None: @@ -1084,11 +1087,10 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): latmask = init_mask.convert('RGB').resize((self.init_latent.shape[3], self.init_latent.shape[2])) latmask = np.moveaxis(np.array(latmask, dtype=np.float32), 2, 0) / 255 latmask = latmask[0] - latmask = np.around(latmask) latmask = np.tile(latmask[None], (4, 1, 1)) + latmask = np.around(latmask) self.mask = torch.asarray(1.0 - latmask).to(device=shared.device, dtype=self.sd_model.dtype) self.nmask = torch.asarray(latmask).to(device=shared.device, dtype=self.sd_model.dtype) - # this needs to be fixed to be done in sample() using actual seeds for batches if self.inpainting_fill == 2: self.init_latent = self.init_latent * self.mask + create_random_tensors(self.init_latent.shape[1:], all_seeds[0:self.init_latent.shape[0]]) * self.nmask elif self.inpainting_fill == 3: diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 75b930529..4fcf36ac9 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -4,6 +4,7 @@ import modules.devices as devices import modules.shared as shared import modules.sd_samplers as sd_samplers import modules.sd_models as sd_models +import modules.images as images from modules.lora_diffusers import lora_state, unload_diffusers_lora from modules.processing import StableDiffusionProcessing @@ -16,12 +17,12 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro shared.state.sampling_steps = p.steps shared.state.current_latent = latents - def vae_decode(latents, model): + def vae_decode(latents, model, output_type='np'): if hasattr(model, 'vae'): shared.log.debug(f'Diffusers VAE decode: name={model.vae.config.get("_name_or_path", "default")} upcast={model.vae.config.get("force_upcast", None)}') decoded = model.vae.decode(latents / model.vae.config.scaling_factor, return_dict=False)[0] - images = model.image_processor.postprocess(decoded, output_type='np') - return images + imgs = model.image_processor.postprocess(decoded, output_type=output_type) + return imgs else: return latents @@ -54,16 +55,23 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro for arg in kwargs: if arg in possible: args[arg] = kwargs[arg] - shared.log.debug(f'Diffuser pipeline: {pipeline.__class__.__name__} possible={possible}') + else: + pass + # shared.log.debug(f'Diffuser not supported: pipeline={pipeline.__class__.__name__} task={sd_models.get_diffusers_task(model)} arg={arg}') + # shared.log.debug(f'Diffuser pipeline: {pipeline.__class__.__name__} possible={possible}') clean = args.copy() clean.pop('callback', None) clean.pop('callback_steps', None) - clean.pop('image', None) - clean.pop('mask_image', None) - clean.pop('prompt', None) - clean.pop('negative_prompt', None) + if 'image' in clean: + clean['image'] = type(clean['image']) + if 'mask_image' in clean: + clean['mask_image'] = type(clean['mask_image']) + if 'prompt' in clean: + clean['prompt'] = len(clean['prompt']) + if 'negative_prompt' in clean: + clean['negative_prompt'] = len(clean['negative_prompt']) clean['generator'] = generator_device - shared.log.debug(f'Diffuser pipeline: {pipeline.__class__.__name__} set={clean}') + shared.log.debug(f'Diffuser pipeline: {pipeline.__class__.__name__} task={sd_models.get_diffusers_task(model)} set={clean}') return args if (not hasattr(shared.sd_model.scheduler, 'name')) or (shared.sd_model.scheduler.name != p.sampler_name) and (p.sampler_name != 'Default'): @@ -79,10 +87,9 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.TEXT_2_IMAGE: task_specific_kwargs = {"height": p.height, "width": p.width} elif sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE: - task_specific_kwargs = {"image": p.init_images[0], "strength": p.denoising_strength} + task_specific_kwargs = {"image": p.init_images, "strength": p.denoising_strength} elif sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.INPAINTING: - # TODO(PVP): change out to latents once possible with `diffusers` - task_specific_kwargs = {"image": p.init_images[0], "mask_image": p.image_mask, "strength": p.denoising_strength} + task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength} # TODO diffusers use transformers for prompt parsing # from modules.prompt_parser import parse_prompt_attention @@ -124,13 +131,12 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro for i in range(len(output.images)): - """ - # TODO save before refiner if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_refiner and hasattr(shared.sd_model, 'vae'): - info=infotext(n, i) - image = decode_first_stage(shared.sd_model, output.images[i].to(dtype=devices.dtype_vae)) - images.save_image(image, path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-refiner") - """ + from modules.processing import create_infotext + info=create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, [], iteration=p.iteration, position_in_batch=i) + decoded = vae_decode(output.images, shared.sd_model, output_type='pil') + for i in range(len(decoded)): + images.save_image(decoded[i], path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-refiner") pipe_args = set_pipeline_args( model=shared.sd_refiner, diff --git a/modules/shared.py b/modules/shared.py index 98b3a3a82..1ad2d54f1 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -455,7 +455,7 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "save_txt": OptionInfo(False, "Create text file next to every image with generation parameters"), "save_log_fn": OptionInfo("", "Create JSON log file for each saved image", component_args=hide_dirs), "save_images_before_highres_fix": OptionInfo(False, "Save copy of image before applying highres fix"), - # "save_images_before_refiner": OptionInfo(False, "Save copy of image before running refiner"), + "save_images_before_refiner": OptionInfo(False, "Save copy of image before running refiner"), "save_images_before_face_restoration": OptionInfo(False, "Save copy of image before doing face restoration"), "save_images_before_color_correction": OptionInfo(False, "Save copy of image before applying color correction"), "save_mask": OptionInfo(False, "Save copy of the inpainting greyscale mask"), @@ -532,7 +532,7 @@ options_templates.update(options_section(('live-preview', "Live Previews"), { options_templates.update(options_section(('sampler-params', "Sampler Settings"), { "show_samplers": OptionInfo(["Default", "Euler a", "UniPC", "DEIS", "DDIM", "DPM 1S", "DPM 2M", "DPM++ 2M SDE", "DPM++ 2M SDE Karras", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers() if x.name != "PLMS"]}), - "fallback_sampler": OptionInfo("Euler a", "Secondary sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}), + # "fallback_sampler": OptionInfo("Euler a", "Secondary sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}), # "force_latent_sampler": OptionInfo("None", "Force latent upscaler sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}), 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}), 'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}), diff --git a/modules/ui.py b/modules/ui.py index bb2c4f300..a31fa104c 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -383,7 +383,7 @@ def create_ui(startup_timer = None): with FormGroup(visible=False, elem_id="txt2img_second_pass") as hr_options: hr_second_pass_steps, latent_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers, "txt2img", False) with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.3, elem_id="txt2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.3, elem_id="txt2img_denoising_strength") with FormRow(): hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) @@ -666,7 +666,7 @@ def create_ui(startup_timer = None): image_cfg_scale = gr.Slider(minimum=0, maximum=30.0, step=0.05, label='Image CFG Scale', value=1.5, elem_id="img2img_image_cfg_scale") diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance Rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") with FormRow(): - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") refiner_denoise_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="txt2img_refiner_denoise_start") refiner_denoise_end = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise end', value=1.0, elem_id="txt2img_refiner_denoise_end") diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index 3984f4077..40cdb339a 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -13,15 +13,6 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): def list_items(self): for name, path in shared.hypernetworks.items(): path, _ext = os.path.splitext(path) - print('HERE', { - "name": name, - "filename": path, - "preview": self.find_preview(path), - "description": self.find_description(path), - "search_term": self.search_terms_from_path(path), - "prompt": json.dumps(f""), - "local_preview": f"{path}.preview.{shared.opts.samples_format}", - }) yield { "name": name, "filename": path, diff --git a/package.json b/package.json index f19c987d5..c9333006c 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "url": "git+https://github.com/vladmandic/automatic.git" }, "devDependencies": { + "esbuild": "^0.18.15", "eslint": "^8.44.0", "eslint-config-airbnb-base": "^15.0.0" },