-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
285 lines
8.4 KiB
JavaScript
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();
|
|
}
|
|
|
|
},
|
|
}); |