Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add model preview feature #2211

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@
display: none;
}

#stylePreviewOverlay {
#stylePreviewOverlay,
#modelPreviewOverlay {
opacity: 0;
pointer-events: none;
width: 128px;
Expand All @@ -215,6 +216,20 @@
transition: transform 0.1s ease, opacity 0.3s ease;
}

#stylePreviewOverlay.lower-half {
#stylePreviewOverlay.lower-half,
#modelPreviewOverlay.lower-half {
transform: translate(-140px, -140px);
}

#modelPreviewOverlay {
z-index: 10000000000 !important;
justify-content: center;
display: flex;
align-items: center;
background-color: rgba(0, 0, 0, 0.7) !important;
color: white;
padding: 5px;
max-width: 100%;
overflow-wrap: break-word;
word-break: break-all;
}
144 changes: 124 additions & 20 deletions javascript/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ document.addEventListener("DOMContentLoaded", function() {
});
mutationObserver.observe(gradioApp(), {childList: true, subtree: true});
initStylePreviewOverlay();
initModelPreviewOverlay();
});

/**
Expand All @@ -146,38 +147,141 @@ document.addEventListener('keydown', function(e) {
}
});

function initStylePreviewOverlay() {
let overlayVisible = false;
const samplesPath = document.querySelector("meta[name='samples-path']").getAttribute("content")
// Utility functions
function formatImagePath(name, templateImagePath, replacedValue = "fooocus_v2") {
return templateImagePath.replace(replacedValue, name.toLowerCase().replaceAll(" ", "_")).replaceAll("\\", "\\\\");
}

function createOverlay(id) {
const overlay = document.createElement('div');
overlay.id = 'stylePreviewOverlay';
overlay.id = id;
document.body.appendChild(overlay);
document.addEventListener('mouseover', function(e) {
const label = e.target.closest('.style_selections label');
return overlay;
}

function setImageBackground(overlay, url) {
unsetOverlayAsTooltip(overlay)
overlay.style.backgroundImage = `url("${url}")`;
}

function setOverlayAsTooltip(overlay, altText) {
// Set the text content and any dynamic styles
overlay.textContent = altText;
overlay.style.width = 'fit-content';
overlay.style.height = 'fit-content';
// Note: Other styles are already set via CSS
}

function unsetOverlayAsTooltip(overlay) {
// Clear the text content and reset any dynamic styles
overlay.textContent = '';
overlay.style.width = '128px';
overlay.style.height = '128px';
// Note: Other styles are managed via CSS
}

function handleMouseMove(overlay) {
return function(e) {
if (overlay.style.opacity !== "1") return;
overlay.style.left = `${e.clientX}px`;
overlay.style.top = `${e.clientY}px`;
overlay.className = e.clientY > window.innerHeight / 2 ? "lower-half" : "upper-half";
};
}

// Image path retrieval for models
const getModelImagePath = selectedItemText => {
selectedItemText = selectedItemText.replace("✓\n", "")

let imagePath = null;

if (previewsCheckpoint)
imagePath = previewsCheckpoint[selectedItemText]

if (previewsLora && !imagePath)
imagePath = previewsLora[selectedItemText]

return imagePath;
};

// Mouse over handlers for different overlays
function handleMouseOverModelPreviewOverlay(overlay, elementSelector, templateImagePath) {
return function(e) {
const targetElement = e.target.closest(elementSelector);
if (!targetElement) return;

targetElement.removeEventListener("mouseout", onMouseLeave);
targetElement.addEventListener("mouseout", onMouseLeave);

overlay.style.opacity = "1";
const selectedItemText = targetElement.innerText;
if (selectedItemText) {
let imagePath = getModelImagePath(selectedItemText);
if (imagePath) {
imagePath = formatImagePath(imagePath, templateImagePath, "sdxl_styles/samples/fooocus_v2.jpg");
setImageBackground(overlay, imagePath);
} else {
setOverlayAsTooltip(overlay, selectedItemText);
}
}

function onMouseLeave() {
overlay.style.opacity = "0";
overlay.style.backgroundImage = "";
targetElement.removeEventListener("mouseout", onMouseLeave);
}
};
}

function handleMouseOverStylePreviewOverlay(overlay, elementSelector, templateImagePath) {
return function(e) {
const label = e.target.closest(elementSelector);
if (!label) return;

label.removeEventListener("mouseout", onMouseLeave);
label.addEventListener("mouseout", onMouseLeave);
overlayVisible = true;

overlay.style.opacity = "1";

const originalText = label.querySelector("span").getAttribute("data-original-text");
const name = originalText || label.querySelector("span").textContent;
overlay.style.backgroundImage = `url("${samplesPath.replace(
"fooocus_v2",
name.toLowerCase().replaceAll(" ", "_")
).replaceAll("\\", "\\\\")}")`;
let name = originalText || label.querySelector("span").textContent;
let imagePath = formatImagePath(name, templateImagePath);

overlay.style.backgroundImage = `url("${imagePath}")`;

function onMouseLeave() {
overlayVisible = false;
overlay.style.opacity = "0";
overlay.style.backgroundImage = "";
label.removeEventListener("mouseout", onMouseLeave);
}
});
document.addEventListener('mousemove', function(e) {
if(!overlayVisible) return;
overlay.style.left = `${e.clientX}px`;
overlay.style.top = `${e.clientY}px`;
overlay.className = e.clientY > window.innerHeight / 2 ? "lower-half" : "upper-half";
});
};
}

// Initialization functions for different overlays
function initModelPreviewOverlay() {
const templateImagePath = document.querySelector("meta[name='samples-path']").getAttribute("content");
const modelOverlay = createOverlay('modelPreviewOverlay');

document.addEventListener('mouseover', handleMouseOverModelPreviewOverlay(
modelOverlay,
'.model_selections .item',
templateImagePath
));

document.addEventListener('mousemove', handleMouseMove(modelOverlay));
}

function initStylePreviewOverlay() {
const templateImagePath = document.querySelector("meta[name='samples-path']").getAttribute("content");
const styleOverlay = createOverlay('stylePreviewOverlay');

document.addEventListener('mouseover', handleMouseOverStylePreviewOverlay(
styleOverlay,
'.style_selections label',
templateImagePath
));

document.addEventListener('mousemove', handleMouseMove(styleOverlay));
}

/**
Expand Down
6 changes: 5 additions & 1 deletion modules/async_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def worker():
from modules.util import remove_empty_str, HWC3, resize_image, \
get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate
from modules.upscaler import perform_upscale
from modules.model_previewer import add_preview_by_attempt

try:
async_gradio_app = shared.gradio_root
Expand Down Expand Up @@ -799,7 +800,10 @@ def callback(step, x0, x, total_steps, y):
if n != 'None':
d.append((f'LoRA {li + 1}', f'{n} : {w}'))
d.append(('Version', 'v' + fooocus_version.version))
log(x, d)
image_location = log(x, d)

if modules.config.use_add_model_previews:
add_preview_by_attempt(base_model_name, refiner_model_name, loras, image_location)

yield_result(async_task, imgs, do_not_show_finished_images=len(tasks) == 1)
except ldm_patched.modules.model_management.InterruptProcessingException as e:
Expand Down
19 changes: 17 additions & 2 deletions modules/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from modules.model_loader import load_file_from_url
from modules.util import get_files_from_folder

from modules.model_previewer import cleanup as cleanup_model_previews

config_path = os.path.abspath("./config.txt")
config_example_path = os.path.abspath("config_modification_tutorial.txt")
Expand Down Expand Up @@ -316,6 +316,16 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
default_value=-1,
validator=lambda x: isinstance(x, int)
)
use_cleanup_model_previews = get_config_item_or_set_default(
key='use_cleanup_model_previews',
default_value=False,
validator=lambda x: x == False or x == True
)
use_add_model_previews = get_config_item_or_set_default(
key='use_add_model_previews',
default_value=True,
validator=lambda x: x == False or x == True
)
example_inpaint_prompts = get_config_item_or_set_default(
key='example_inpaint_prompts',
default_value=[
Expand All @@ -342,6 +352,9 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
"default_prompt_negative",
"default_styles",
"default_aspect_ratio",
"default_aspect_ratio",
"use_cleanup_model_previews"
"use_add_model_previews",
"checkpoint_downloads",
"embeddings_downloads",
"lora_downloads",
Expand Down Expand Up @@ -514,5 +527,7 @@ def downloading_upscale_model():
)
return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin')


update_all_model_names()

if use_cleanup_model_previews:
cleanup_model_previews()
Loading