Files
JaredTherriault-ComfyUI-JNodes/web/js/ImageDrawer/ImageDrawer.js
T
JaredTherriault f0e9347640 Major video update, ConfigSettings improvements, js async cancellation, load optimizations
-Roll UiSettings into SettingsManager
-Account for ConfigSettings that are objects by loading default values then loading config value son top
-Set ConfigSetting values using JSON.stringify
-Separate flyout generation into generic function
-Add video settings flyout in subfolderexplorer contexts
-Add option to control slider range value label fractional digits
-Update default slider range values to reflect 1-100 values rather than 0-1
-Rename ImageDrawerContext description to tooltip
-Update 'flex' alignment to 'normal' in context toolbars
-Automatically cancel async js operations when switching contexts
-blur or unfocus toolbar buttons on click to avoid accidental clearing or refreshing
-Get setting_ModelCardAspectRatio value as number
-Implement method to avoid lazy loading of images
-Lay some groundwork for unimplemented cancellation requests for python tasks
-Separated image list and search variables and functions to a separate script, ImageListAndSearch
-Update extra networks toolbar buttons to have a bespoke class and use specific cursor styles
-Update ImageElements 'complete' to 'bComplete' to differentiate from the img property
-Add lastSeekTime property initialization to img element
-Don't run onload if image element is already bComplete
-Sort and execute search after image is loaded (if requested). This allows search and sort to work with all of the meta data in the image.
-Click videos to pause/play
-Double click videos to toggle fullscreen
-Mute videos based on video options
-Set video volume from video options flyout
-Use scrollwheel on video to seek forward or back (seek direction depends on video options)
-Update imports
2024-04-17 14:58:59 -07:00

285 lines
8.4 KiB
JavaScript

import { app } from "/scripts/app.js";
import { $el } from "/scripts/ui.js";
import * as ExtraNetworks from "./ExtraNetworks.js";
import * as ContextSelector from "./ContextSelector.js";
import * as Sorting from "./Sorting.js";
import {
ImageDrawerConfigSetting, setupUiSettings, createDrawerSelectionWidget,
setting_bEnabled, setting_bMasterVisibility, setting_DrawerAnchor,
createFlyoutHandle, createLabeledSliderRange, options_LabeledSliderRange } from "../common/SettingsManager.js";
import { clearAndExecuteSearch, createSearchBar, executeSearchWithEnteredSearchText, getImageListElement, setSearchTextAndExecute } from "./ImageListAndSearch.js";
// Attribution: pythongsssss's Image Feed. So much brilliance in that original script.
//Singletons
let imageDrawer;
let DrawerOptionsFlyout;
let ImageDrawerContextToolbar;
let drawerWidthSlider;
let drawerHeightSlider;
let columnSlider;
const _minimumDrawerSize = 15;
const _maximumDrawerSize = 100;
let setting_ColumnCount = new ImageDrawerConfigSetting("ImageSize", 4);
let setting_DrawerHeight = new ImageDrawerConfigSetting("DrawerHeight", 25);
let setting_DrawerWidth = new ImageDrawerConfigSetting("DrawerWidth", 25);
export function getColumnCount() {
return setting_ColumnCount.value;
}
export function setColumnCount(value, setColumnCountSliderValue = true) {
setting_ColumnCount.value = value;
imageDrawer?.style.setProperty("--column-count", value);
if (setColumnCountSliderValue && columnSlider) {
columnSlider.max = Math.max(10, value, columnSlider.max);
columnSlider.value = value;
columnSlider.title = `Controls the number of columns in the drawer (${value} columns).\nClick label to set custom value.`;
}
}
export function getDrawerWidth() {
return setting_DrawerWidth.value;
}
export function setDrawerWidth(value, setDrawerWidthSliderValue = true) {
setting_DrawerWidth.value = value;
imageDrawer?.style.setProperty("--drawer-width", value);
if (setDrawerWidthSliderValue && drawerWidthSlider) {
drawerWidthSlider.value = value;
drawerWidthSlider.title = `Controls the maximum width of the drawer panel (${value}vw)`;
}
}
export function getDrawerHeight() {
return setting_DrawerHeight.value;
}
export function setDrawerHeight(value, setDrawerHeightSliderValue = true) {
setting_DrawerHeight.value = value;
imageDrawer?.style.setProperty("--drawer-height", value);
if (setDrawerHeightSliderValue && drawerHeightSlider) {
drawerHeightSlider.value = value;
drawerHeightSlider.title = `Controls the maximum height of the drawer panel (${value}vh)`;
}
}
function setDrawerAnchor(value) {
setting_DrawerAnchor.value = value;
imageDrawer.className =
`JNodes-image-drawer JNodes-image-drawer--${value}`;
}
export function setContextToolbarWidget(widget) {
ImageDrawerContextToolbar.replaceChildren(widget);
}
export function setSortingOptions(options) {
ImageDrawerContextToolbar.replaceChildren(widget);
}
const createDrawerOptionsFlyout = () => {
let widthSliderOptions = new options_LabeledSliderRange();
widthSliderOptions.bPrependValueLabel = true;
widthSliderOptions.min = _minimumDrawerSize;
widthSliderOptions.max = _maximumDrawerSize;
widthSliderOptions.value = getDrawerWidth();
widthSliderOptions.oninput = (e) => {
setDrawerWidth(e.target.valueAsNumber, false);
};
drawerWidthSlider = createLabeledSliderRange(widthSliderOptions);
setDrawerWidth(getDrawerWidth());
let heightSliderOptions = new options_LabeledSliderRange();
heightSliderOptions.bPrependValueLabel = true;
heightSliderOptions.min = _minimumDrawerSize;
heightSliderOptions.max = _maximumDrawerSize;
heightSliderOptions.value = getDrawerHeight();
heightSliderOptions.oninput = (e) => {
setDrawerHeight(e.target.valueAsNumber, false);
};
drawerHeightSlider = createLabeledSliderRange(heightSliderOptions);
setDrawerHeight(getDrawerHeight());
let columnSliderOptions = new options_LabeledSliderRange();
columnSliderOptions.bPrependValueLabel = true;
columnSliderOptions.min = 1;
columnSliderOptions.max = 10;
columnSliderOptions.value = getColumnCount();
columnSliderOptions.oninput = (e) => {
setColumnCount(e.target.valueAsNumber, false);
};
columnSlider = createLabeledSliderRange(columnSliderOptions);
setColumnCount(getColumnCount());
let flyout = createFlyoutHandle("👁️");
DrawerOptionsFlyout = flyout.handle;
flyout.menu.appendChild(
$el("tr.size-control.drawer-width-control", [
$el('td', [$el("span", {
textContent: 'Drawer Width',
})]),
$el('td', [drawerWidthSlider])
]));
flyout.menu.appendChild(
$el("tr.size-control.drawer-height-control", [
$el('td', [$el("span", {
textContent: 'Drawer Height',
})]),
$el('td', [drawerHeightSlider])
]));
flyout.menu.appendChild(
$el("tr.size-control.column-count-control", [
$el('td', [$el("a", {
textContent: "Column count",
style: {
cursor: "pointer",
textDecoration: "underline",
},
onclick: () => {
const value = +prompt("Enter custom column count", 20);
if (!isNaN(value)) {
setColumnCount(value);
}
},
})]),
$el('td', [columnSlider])
]));
flyout.menu.appendChild(
// Anchor Select
$el("tr.drawer-anchor-control", [
$el('td', [$el("span", {
textContent: "Image Drawer Anchor:",
})]),
$el('td', [createDrawerSelectionWidget((e) => { setDrawerAnchor(e.target.value); })])
]));
};
app.registerExtension({
name: "JNodes.ImageDrawer",
async setup() {
setupUiSettings((e) => { setDrawerAnchor(e.target.value); });
if (!setting_bEnabled.value) {
return;
}
// Remove the drawer widget from view, can be re-opened with showButton
const hideButton = $el("button.JNodes-image-drawer-btn.hide-btn", {
textContent: "❌",
onclick: () => {
imageDrawer.style.display = "none";
showButton.style.display = "unset";
setting_bMasterVisibility.value = false;
},
style: {
width: "fit-content",
padding: '3px',
},
});
// Get loras right at the start so we ensure we have to wait less when switching to loras context
ExtraNetworks.getLoras();
// The main drawer widget
imageDrawer = $el("div.JNodes-image-drawer", {
parent: document.body
});
// Initialize Anchor
const drawerStartingAnchor = setting_DrawerAnchor.value;
imageDrawer.className =
`JNodes-image-drawer JNodes-image-drawer--${drawerStartingAnchor}`;
// Resizing / View options
createDrawerOptionsFlyout();
// Search bar
const SearchBarClearButton = $el("button.JNodes-search-bar-clear-btn", {
textContent: "❌",
title: "Clear Search",
onclick: clearAndExecuteSearch
});
async function onClickSearchRandomizeButton() {
let loraDicts = await ExtraNetworks.getLoras();
const loraKeys = Object.keys(loraDicts);
const randomIndex = Math.floor(Math.random() * loraKeys.length);
setSearchTextAndExecute(loraKeys[randomIndex]);
}
const RandomizeButton = $el("button.JNodes-search-randomize-btn", {
textContent: "🎲",
title: "Random Suggestion",
onclick: onClickSearchRandomizeButton
});
const SearchBarGroup =
$el("div.JNodes-search-bar-group", {
style: {
width: '100%',
display: 'flex',
flexDirection: 'row',
}
}, [
createSearchBar(), SearchBarClearButton, RandomizeButton
]);
const BasicControlsGroup =
$el("div.JNodes-image-drawer-basic-controls-group", [hideButton, DrawerOptionsFlyout]);
ImageDrawerContextToolbar =
$el("div.JNodes-image-drawer-context-toolbar");
function makeDropDownComboContainer() {
// Context and sorting Dropdowns
const sorting = Sorting.makeSortingWidget(); // Sorting first since contexts act upon sorting
sorting.style.width = '50%';
const context = ContextSelector.createContextSelector();
context.style.width = '50%';
const DropDownComboContainer = $el("div", {
style: {
display: "flex",
flexDirection: "row"
}
}, [context, sorting]);
return DropDownComboContainer;
}
const ImageDrawerMenu =
$el("div.JNodes-image-drawer-menu", {
style: {
minHeight: 'fit-content',
minWidth: 'fit-content',
position: 'relative',
flex: '0 1 min-content',
display: 'flex',
gap: '3px',
padding: '3px',
justifyContent: 'flex-start',
},
}, [
BasicControlsGroup,
makeDropDownComboContainer(),
SearchBarGroup,
ImageDrawerContextToolbar,
]);
imageDrawer.append(ImageDrawerMenu, getImageListElement());
// If not supposed to be visible on startup, close it
if (!setting_bMasterVisibility.value) {
hideButton.onclick();
}
},
});