Layout and button changes

- In Model View, moved "Set as Preview" button below radio buttons and disabled when Default selected.
- General CSS layout improvements.
This commit is contained in:
Christian Bastian
2024-02-22 15:53:58 -05:00
parent d951a508ed
commit b7747a163a
3 changed files with 183 additions and 146 deletions

View File

@@ -651,10 +651,14 @@ async def move_model(request):
if new_path is None: if new_path is None:
return web.json_response({ "success": False }) return web.json_response({ "success": False })
new_path, _ = search_path_to_system_path(new_path) new_path, _ = search_path_to_system_path(new_path)
if new_path is None:
return web.json_response({ "success": False })
if not os.path.isdir(new_path): if not os.path.isdir(new_path):
return web.json_response({ "success": False }) return web.json_response({ "success": False })
new_file = os.path.join(new_path, filename) new_file = os.path.join(new_path, filename)
if old_file == new_file:
return web.json_response({ "success": False })
try: try:
shutil.move(old_file, new_file) shutil.move(old_file, new_file)
except ValueError as e: except ValueError as e:

View File

@@ -366,6 +366,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex: 1; flex: 1;
min-width: 0;
} }
.model-manager .model-select-dropdown { .model-manager .model-select-dropdown {
@@ -403,9 +404,15 @@
.model-manager .search-dropdown > p { .model-manager .search-dropdown > p {
margin: 0; margin: 0;
padding-left: 20px; padding: 0.85em 20px;
padding-top: 0.85em; min-width: 0;
padding-bottom: 0.85em; }
.model-manager .search-dropdown > p {
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
}
.model-manager .search-dropdown > p::-webkit-scrollbar {
display: none; /* Safari and Chrome */
} }
.model-manager .search-dropdown > p.search-dropdown-selected { .model-manager .search-dropdown > p.search-dropdown-selected {

View File

@@ -721,7 +721,7 @@ class DirectoryDropdown {
dropdown.append.apply(dropdown, innerHtml); dropdown.append.apply(dropdown, innerHtml);
// TODO: handle when dropdown is near the bottom of the window // TODO: handle when dropdown is near the bottom of the window
const inputRect = input.getBoundingClientRect(); const inputRect = input.getBoundingClientRect();
dropdown.style.minWidth = inputRect.width + "px"; dropdown.style.width = inputRect.width + "px";
dropdown.style.top = (input.offsetTop + inputRect.height) + "px"; dropdown.style.top = (input.offsetTop + inputRect.height) + "px";
dropdown.style.left = input.offsetLeft + "px"; dropdown.style.left = input.offsetLeft + "px";
dropdown.style.display = "block"; dropdown.style.display = "block";
@@ -1338,7 +1338,7 @@ function updateRadioPreview(previewImageContainer, step) {
* @param {String[]} defaultPreviews * @param {String[]} defaultPreviews
* @returns {[]} * @returns {[]}
*/ */
function radioGroupImageSelect(uniqueName, defaultPreviews, defaultChanges=false) { function radioGroupImageSelect(uniqueName, defaultPreviews) {
const defaultImageCount = defaultPreviews.length; const defaultImageCount = defaultPreviews.length;
const el_defaultUri = $el("div", { const el_defaultUri = $el("div", {
@@ -1457,10 +1457,10 @@ function radioGroupImageSelect(uniqueName, defaultPreviews, defaultChanges=false
el_previewButtons, el_previewButtons,
]); ]);
const PREVIEW_NONE = "No Preview";
const PREVIEW_DEFAULT = "Default"; const PREVIEW_DEFAULT = "Default";
const PREVIEW_URL = "URL";
const PREVIEW_UPLOAD = "Upload"; const PREVIEW_UPLOAD = "Upload";
const PREVIEW_URL = "URL";
const PREVIEW_NONE = "No Preview";
const el_radioButtons = $radioGroup({ const el_radioButtons = $radioGroup({
name: uniqueName, name: uniqueName,
@@ -1476,36 +1476,48 @@ function radioGroupImageSelect(uniqueName, defaultPreviews, defaultChanges=false
el_urlImage.style.display = "none"; el_urlImage.style.display = "none";
switch (value) { switch (value) {
case PREVIEW_NONE:
default:
el_noImage.style.display = "block";
break;
case PREVIEW_DEFAULT: case PREVIEW_DEFAULT:
el_defaultImages.style.display = "block"; el_defaultImages.style.display = "block";
el_previewButtons.style.display = el_defaultImages.children.length > 1 ? "block" : "none"; el_previewButtons.style.display = el_defaultImages.children.length > 1 ? "block" : "none";
break; break;
case PREVIEW_URL:
el_custom.style.display = "flex";
el_urlImage.style.display = "block";
break;
case PREVIEW_UPLOAD: case PREVIEW_UPLOAD:
el_upload.style.display = "flex"; el_upload.style.display = "flex";
el_uploadImage.style.display = "block"; el_uploadImage.style.display = "block";
break; break;
case PREVIEW_URL:
el_custom.style.display = "flex";
el_urlImage.style.display = "block";
break;
case PREVIEW_NONE:
default:
el_noImage.style.display = "block";
break;
} }
}, },
options: (() => { options: (() => {
const radios = []; const radios = [];
radios.push({ value: PREVIEW_NONE });
if (defaultImageCount > 0) { if (defaultImageCount > 0) {
radios.push({ value: PREVIEW_DEFAULT }); radios.push({ value: PREVIEW_DEFAULT });
} }
radios.push({ value: PREVIEW_URL });
radios.push({ value: PREVIEW_UPLOAD }) radios.push({ value: PREVIEW_UPLOAD })
radios.push({ value: PREVIEW_URL });
radios.push({ value: PREVIEW_NONE });
return radios; return radios;
})(), })(),
}); });
const defaultIsChecked = () => {
const children = el_radioButtons.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
const radioButton = child.children[0];
if (radioButton.value === PREVIEW_DEFAULT) {
return radioButton.checked;
}
};
return false;
}
if (defaultImageCount > 0) { if (defaultImageCount > 0) {
const children = el_radioButtons.children; const children = el_radioButtons.children;
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
@@ -1583,7 +1595,8 @@ function radioGroupImageSelect(uniqueName, defaultPreviews, defaultChanges=false
]), ]),
]); ]);
return [el_radioGroup, el_preview, getImage, el_defaultUri, resetModelInfoPreview]; // TODO: make this an object?
return [el_radioGroup, el_radioButtons, el_preview, getImage, el_defaultUri, resetModelInfoPreview, defaultIsChecked];
} }
class ModelManager extends ComfyDialog { class ModelManager extends ComfyDialog {
@@ -1593,8 +1606,10 @@ class ModelManager extends ComfyDialog {
/** @type {HTMLDivElement} */ modelInfoUrl: null, /** @type {HTMLDivElement} */ modelInfoUrl: null,
/** @type {HTMLDivElement} */ modelInfoOverwrite: null, /** @type {HTMLDivElement} */ modelInfoOverwrite: null,
/** @type {HTMLDivElement} */ modelInfos: null, /** @type {HTMLDivElement} */ modelInfos: null,
modelInfoRadioGroup: null,
modelInfoPreview: null, modelInfoPreview: null,
modelInfoDefaultUri: null, modelInfoDefaultUri: null,
setAsPreviewButton: null,
/** @type {HTMLDivElement} */ modelGrid: null, /** @type {HTMLDivElement} */ modelGrid: null,
/** @type {HTMLSelectElement} */ modelTypeSelect: null, /** @type {HTMLSelectElement} */ modelTypeSelect: null,
@@ -1635,6 +1650,7 @@ class ModelManager extends ComfyDialog {
#systemSeparator = null; #systemSeparator = null;
#resetModelInfoPreview = () => {}; #resetModelInfoPreview = () => {};
#modelInfoDefaultIsChecked = () => { return false; };
constructor() { constructor() {
super(); super();
@@ -1657,7 +1673,7 @@ class ModelManager extends ComfyDialog {
true, true,
); );
const [el_radioGroup, el_preview, getImage, el_defaultUri, resetModelInfoPreview] = radioGroupImageSelect( const [el_radioGroup, el_radioButtons, el_preview, getImage, el_defaultUri, resetModelInfoPreview, defaultIsChecked] = radioGroupImageSelect(
"model-info-preview-model-FYUIKMNVB", "model-info-preview-model-FYUIKMNVB",
[imageUri()], [imageUri()],
); );
@@ -1666,6 +1682,70 @@ class ModelManager extends ComfyDialog {
this.#el.modelInfoPreview = el_preview; this.#el.modelInfoPreview = el_preview;
this.#el.modelInfoDefaultUri = el_defaultUri; this.#el.modelInfoDefaultUri = el_defaultUri;
this.#resetModelInfoPreview = resetModelInfoPreview; this.#resetModelInfoPreview = resetModelInfoPreview;
this.#modelInfoDefaultIsChecked = defaultIsChecked;
const setAsPreviewButton = $el("button", {
$: (el) => (this.#el.setAsPreviewButton = el),
textContent: "Set as Preview",
onclick: async(e) => {
const confirmation = window.confirm("Change preview image PERMANENTLY?");
let updatedPreview = false;
if (confirmation) {
e.target.disabled = true;
const container = this.#el.modelInfoContainer;
const path = container.dataset.path;
const imageUrl = getImage();
if (imageUrl === imageUri()) {
const encodedPath = encodeURIComponent(path);
updatedPreview = await request(
`/model-manager/preview/delete?path=${encodedPath}`,
{
method: "POST",
body: JSON.stringify({}),
}
)
.then((result) => {
return result["success"];
})
.catch((err) => {
return false;
});
}
else {
const formData = new FormData();
formData.append("path", path);
const image = imageUrl[0] == "/" ? "" : imageUrl;
formData.append("image", image);
updatedPreview = await request(
`/model-manager/preview/set`,
{
method: "POST",
body: formData,
}
)
.then((result) => {
return result["success"];
})
.catch((err) => {
return false;
});
}
if (updatedPreview) {
this.#modelTab_updateModels();
this.#el.modelInfoDefaultUri.dataset.noimage = imageUri();
this.#resetModelInfoPreview();
this.#el.modelInfoView.style.display = "none";
}
e.target.disabled = false;
}
buttonAlert(e.target, updatedPreview);
},
});
el_radioButtons.addEventListener("change", (e) => {
setAsPreviewButton.style.display = defaultIsChecked() ? "none" : "block";
});
this.element = $el( this.element = $el(
"div.comfy-modal.model-manager", "div.comfy-modal.model-manager",
@@ -1678,51 +1758,44 @@ class ModelManager extends ComfyDialog {
$: (el) => (this.#el.modelInfoView = el), $: (el) => (this.#el.modelInfoView = el),
style: { display: "none" }, style: { display: "none" },
}, [ }, [
$el("div", {
style: {
display: "flex",
gap: "8px",
},
}, [
$el("button.icon-button", {
textContent: "🗑︎",
onclick: async(e) => {
const affirmation = "delete";
const confirmation = window.prompt("Type \"" + affirmation + "\" to delete the model PERMANENTLY.\n\nThis includes all image or text files.");
let deleted = false;
if (confirmation === affirmation) {
const container = this.#el.modelInfoContainer;
const path = encodeURIComponent(container.dataset.path);
deleted = await request(
`/model-manager/model/delete?path=${path}`,
{
method: "POST",
}
)
.then((result) => {
const deleted = result["success"];
if (deleted)
{
container.innerHTML = "";
this.#el.modelInfoView.style.display = "none";
this.#modelTab_updateModels();
}
return deleted;
})
.catch((err) => {
return false;
});
}
if (!deleted) {
buttonAlert(e.target, false);
}
},
}),
]),
$el("div.row.tab-header", { $el("div.row.tab-header", {
display: "block", display: "block",
}, [ }, [
$el("div.row.tab-header-flex-block", [ $el("div.row.tab-header-flex-block", [
$el("button.icon-button", {
textContent: "🗑︎",
onclick: async(e) => {
const affirmation = "delete";
const confirmation = window.prompt("Type \"" + affirmation + "\" to delete the model PERMANENTLY.\n\nThis includes all image or text files.");
let deleted = false;
if (confirmation === affirmation) {
const container = this.#el.modelInfoContainer;
const path = encodeURIComponent(container.dataset.path);
deleted = await request(
`/model-manager/model/delete?path=${path}`,
{
method: "POST",
}
)
.then((result) => {
const deleted = result["success"];
if (deleted)
{
container.innerHTML = "";
this.#el.modelInfoView.style.display = "none";
this.#modelTab_updateModels();
}
return deleted;
})
.catch((err) => {
return false;
});
}
if (!deleted) {
buttonAlert(e.target, false);
}
},
}),
$el("div.search-models", [ $el("div.search-models", [
moveDestination, moveDestination,
searchDropdown.element, searchDropdown.element,
@@ -1730,33 +1803,36 @@ class ModelManager extends ComfyDialog {
$el("button", { $el("button", {
textContent: "Move", textContent: "Move",
onclick: async(e) => { onclick: async(e) => {
const container = this.#el.modelInfoContainer; const confirmation = window.confirm("Move this file?");
const moved = await request( let moved = false;
`/model-manager/model/move`, if (confirmation) {
{ const container = this.#el.modelInfoContainer;
method: "POST", moved = await request(
body: JSON.stringify({ `/model-manager/model/move`,
"oldFile": container.dataset.path, {
"newDirectory": moveDestination.value, method: "POST",
}), body: JSON.stringify({
} "oldFile": container.dataset.path,
) "newDirectory": moveDestination.value,
.then((result) => { }),
const moved = result["success"]; }
if (moved) )
{ .then((result) => {
container.innerHTML = ""; const moved = result["success"];
this.#el.modelInfoView.style.display = "none"; if (moved)
this.#modelTab_updateModels(); {
} moveDestination.value = "";
return moved; container.innerHTML = "";
}) this.#el.modelInfoView.style.display = "none";
.catch(err => { this.#modelTab_updateModels();
return false; }
}); return moved;
if (!moved) { })
buttonAlert(e.target, false); .catch(err => {
return false;
});
} }
buttonAlert(e.target, moved);
}, },
}), }),
]), ]),
@@ -1960,73 +2036,23 @@ class ModelManager extends ComfyDialog {
const imagePath = info["Preview"]["path"]; const imagePath = info["Preview"]["path"];
const imageDateModified = info["Preview"]["dateModified"]; const imageDateModified = info["Preview"]["dateModified"];
this.#el.modelInfoDefaultUri.dataset.noimage = imageUri(imagePath, imageDateModified); this.#el.modelInfoDefaultUri.dataset.noimage = imageUri(imagePath, imageDateModified);
this.#resetModelInfoPreview();
} }
else {
this.#el.modelInfoDefaultUri.dataset.noimage = imageUri();
}
this.#resetModelInfoPreview();
const setAsPreviewButton = this.#el.setAsPreviewButton;
setAsPreviewButton.style.display = this.#modelInfoDefaultIsChecked() ? "none" : "block";
innerHtml.push($el("div", [ innerHtml.push($el("div", [
this.#el.modelInfoPreview, this.#el.modelInfoPreview,
$el("div.row.tab-header", [ $el("div.row.tab-header", [
$el("div.row.tab-header-flex-block", [ $el("div.row.tab-header-flex-block", [
$el("button", { this.#el.modelInfoRadioGroup,
textContent: "Set as Preview",
onclick: async(e) => {
const confirmation = window.confirm("Change preview image PERMANENTLY?");
let updatedPreview = false;
if (confirmation) {
e.target.disabled = true;
const container = this.#el.modelInfoContainer;
const path = container.dataset.path;
const imageUrl = getImage();
if (imageUrl === imageUri()) {
const encodedPath = encodeURIComponent(path);
updatedPreview = await request(
`/model-manager/preview/delete?path=${encodedPath}`,
{
method: "POST",
body: JSON.stringify({}),
}
)
.then((result) => {
return result["success"];
})
.catch((err) => {
return false;
});
}
else {
const formData = new FormData();
formData.append("path", path);
const image = imageUrl[0] == "/" ? "" : imageUrl;
formData.append("image", image);
updatedPreview = await request(
`/model-manager/preview/set`,
{
method: "POST",
body: formData,
}
)
.then((result) => {
return result["success"];
})
.catch((err) => {
return false;
});
}
if (updatedPreview) {
this.#modelTab_updateModels();
this.#el.modelInfoDefaultUri.dataset.noimage = imageUri();
this.#resetModelInfoPreview();
this.#el.modelInfoView.style.display = "none";
}
e.target.disabled = false;
}
buttonAlert(e.target, updatedPreview);
},
}),
]), ]),
$el("div.row.tab-header-flex-block", [ $el("div.row.tab-header-flex-block", [
this.#el.modelInfoRadioGroup, setAsPreviewButton,
]), ]),
]), ]),
$el("div", $el("div",
@@ -2314,7 +2340,7 @@ class ModelManager extends ComfyDialog {
* @returns {HTMLDivElement} * @returns {HTMLDivElement}
*/ */
#downloadTab_modelInfo(info, modelTypes, modelDirectories, searchSeparator, id) { #downloadTab_modelInfo(info, modelTypes, modelDirectories, searchSeparator, id) {
const [el_radioGroup, el_preview, getImage, el_defaultUri, resetModelInfoPreview] = radioGroupImageSelect( const [el_radioGroup, el_radioButtons, el_preview, getImage, el_defaultUri, resetModelInfoPreview, defaultIsChecked] = radioGroupImageSelect(
"model-download-info-preview-model" + "-" + id, "model-download-info-preview-model" + "-" + id,
info["images"], info["images"],
); );