Skip to content
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
4 changes: 3 additions & 1 deletion src/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,9 @@ public class ThemesImpl : SettingsOptionsAttribute.AbstractImpl
public bool AutoSwapImagesIncludesFullView = false; // TODO: UserUI

[ConfigComment("A list of what buttons to include directly under images in the main prompt area of the Generate tab.\nOther buttons will be moved into the 'More' dropdown.\nThis should be a comma separated list."
+ "\nThe following options are available: \"Use As Init\", \"Use As Image Prompt\", \"Edit Image\", \"Upscale 2x\", \"Star\", \"Reuse Parameters\", \"Open In Folder\", \"Delete\", \"Download\" \"View In History\", \"Refine Image\", \"Copy Path\""
+ "\nThe following options are available: \"Use As Init\", \"Use As Image Prompt\", \"Edit Image\", \"Upscale 2x\", \"Star\", \"Reuse Parameters\", \"Open In Folder\", \"Delete\", \"Download\", \"View In History\", \"Refine Image\", \"Copy Path\", \"Copy Raw Metadata\""
+ "\nSome buttons like 'Edit Image' may only apply to images and will not show for audio or video files."
+ "\nExtensions can add their own button names here too."
+ "\nThe default is blank, which currently implies 'Use As Init,Edit Image,Star,Reuse Parameters'")]
public string ButtonsUnderMainImages = ""; // TODO: UserUI

Expand Down
66 changes: 46 additions & 20 deletions src/wwwroot/js/genpage/gentab/currentimagehandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,13 @@ function toggleStar(path, rawSrc) {

defaultButtonChoices = 'Use As Init,Edit Image,Star,Reuse Parameters';

let registeredMediaButtons = [];

/** Registers a media button for extensions. 'mediaTypes' filters by type eg ['audio'], null means all. 'isDefault' promotes to visible (vs More dropdown). 'showInHistory' controls whether button appears in the History panel. */
function registerMediaButton(name, action, title = '', mediaTypes = null, isDefault = false, showInHistory = true) {
registeredMediaButtons.push({ name, action, title, mediaTypes, isDefault, showInHistory });
}

function getImageFullSrc(src) {
if (src == null) {
return null;
Expand Down Expand Up @@ -823,9 +830,8 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
forceShowWelcomeMessage();
return;
}
let isVideo = isVideoExt(src);
let isAudio = isAudioExt(src);
if ((smoothAdd || !metadata) && canReparse && !isVideo && !isAudio) {
let mediaType = getMediaType(src);
if ((smoothAdd || !metadata) && canReparse && mediaType == 'image') {
let image = new Image();
image.onload = () => {
if (!metadata) {
Expand All @@ -851,7 +857,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
let img;
let isReuse = false;
let srcTarget;
if (isVideo) {
if (mediaType == 'video') {
container = createDiv(null, 'video-container current-image-img');
curImg.innerHTML = '';
img = document.createElement('video');
Expand All @@ -860,11 +866,11 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
img.autoplay = true;
let sourceObj = document.createElement('source');
srcTarget = sourceObj;
sourceObj.type = isVideo;
sourceObj.type = isVideoExt(src);
img.appendChild(sourceObj);
container.appendChild(img);
}
else if (isAudio) {
else if (mediaType == 'audio') {
curImg.innerHTML = '';
container = createDiv(null, 'audio-container current-image-img');
img = document.createElement('audio');
Expand All @@ -888,10 +894,10 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
container = img;
}
function naturalDim() {
if (isVideo) {
if (mediaType == 'video') {
return [img.videoWidth, img.videoHeight];
}
else if (isAudio) {
else if (mediaType == 'audio') {
return [320, 140];
}
else {
Expand All @@ -907,7 +913,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
}
alignImageDataFormat();
}
if (isVideo || isAudio) {
if (mediaType == 'video' || mediaType == 'audio') {
img.addEventListener('loadeddata', function() {
if (img) {
img.onload();
Expand All @@ -926,7 +932,8 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
let buttons = createDiv(null, 'current-image-buttons');
let imagePathClean = getImageFullSrc(src);
let buttonsChoice = getUserSetting('ButtonsUnderMainImages', '');
if (buttonsChoice == '') {
let isUsingDefaults = buttonsChoice == '';
if (isUsingDefaults) {
buttonsChoice = defaultButtonChoices;
}
let buttonDefs = {};
Expand All @@ -939,17 +946,20 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
}
return normalized;
}
function includeButton(name, action, extraClass = '', title = '') {
buttonDefs[normalizeButtonKey(name)] = { name, action, extraClass, title };
function includeButton(name, action, extraClass = '', title = '', mediaTypes = null) {
buttonDefs[normalizeButtonKey(name)] = { name, action, extraClass, title, mediaTypes };
}
function includeLinkButton(name, href, isDownload = false, title = '') {
buttonDefs[normalizeButtonKey(name)] = { name, href, is_download: isDownload, title: title };
function includeLinkButton(name, href, isDownload = false, title = '', mediaTypes = null) {
buttonDefs[normalizeButtonKey(name)] = { name, href, is_download: isDownload, title, mediaTypes };
}
function renderButtonsFromDefs() {
for (let key of buttonsChoiceOrdered) {
let def = buttonDefs[key];
if (def) {
delete buttonDefs[key];
if (def.mediaTypes && !def.mediaTypes.includes(mediaType)) {
continue;
}
if (def.href) {
let link = document.createElement('a');
link.className = `basic-button${def.extraClass || ''}`;
Expand All @@ -967,6 +977,9 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
}
}
for (let def of Object.values(buttonDefs)) {
if (def.mediaTypes && !def.mediaTypes.includes(mediaType)) {
continue;
}
if (def.href) {
subButtons.push({ key: def.name, href: def.href, is_download: def.is_download, title: def.title });
}
Expand All @@ -982,6 +995,16 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
buttonsChoiceOrdered.push(key);
}
}
if (isUsingDefaults) {
for (let reg of registeredMediaButtons) {
if (reg.isDefault) {
let key = normalizeButtonKey(reg.name);
if (key && !buttonsChoiceOrdered.includes(key)) {
buttonsChoiceOrdered.push(key);
}
}
}
}
let isDataImage = src.startsWith('data:');
includeButton('Use As Init', () => {
let initImageParam = document.getElementById('input_initimage');
Expand Down Expand Up @@ -1019,7 +1042,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
tmpImg.src = img.src;
}
}
}, '', 'Sets this image as the Init Image parameter input');
}, '', 'Sets this image as the Init Image parameter input', ['image', 'video']);
includeButton('Use As Image Prompt', () => {
let altPromptRegion = document.getElementById('alt_prompt_region');
if (!altPromptRegion) {
Expand All @@ -1040,7 +1063,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
});
};
tmpImg.src = img.src;
}, '', 'Uses this image as an Image Prompt input');
}, '', 'Uses this image as an Image Prompt input', ['image']);
includeButton('Edit Image', () => {
let initImageGroupToggle = document.getElementById('input_group_content_initimage_toggle');
if (initImageGroupToggle) {
Expand All @@ -1067,7 +1090,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
}
imageEditor.setBaseImage(img);
imageEditor.activate();
}, '', 'Opens an Image Editor for this image');
}, '', 'Opens an Image Editor for this image', ['image']);
includeButton('Upscale 2x', () => {
toDataURL(img.src, (url => {
let [width, height] = naturalDim();
Expand All @@ -1080,7 +1103,7 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
};
mainGenHandler.doGenerate(input_overrides, { 'initimagecreativity': 0.4 });
}));
}, '', 'Runs an instant generation with this image as the input and scale doubled');
}, '', 'Runs an instant generation with this image as the input and scale doubled', ['image', 'video']);
includeButton('Refine Image', () => {
toDataURL(img.src, (url => {
let input_overrides = {
Expand Down Expand Up @@ -1148,6 +1171,9 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
includeButton(added.label, added.onclick, '', added.title);
}
}
for (let reg of registeredMediaButtons) {
includeButton(reg.name, () => reg.action(src), '', reg.title, reg.mediaTypes);
}
renderButtonsFromDefs();
quickAppendButton(buttons, 'More ⮟', (e, button) => {
let rect = button.getBoundingClientRect();
Expand All @@ -1160,10 +1186,10 @@ function setCurrentImage(src, metadata = '', batchId = '', previewGrow = false,
if (!isReuse) {
curImg.appendChild(container);
curImg.appendChild(extrasWrapper);
if (isVideo) {
if (mediaType == 'video') {
new VideoControls(img);
}
else if (isAudio) {
else if (mediaType == 'audio') {
new AudioControls(img);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/wwwroot/js/genpage/gentab/outputhistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function listOutputHistoryFolderAndFiles(path, isRefresh, callback, depth) {

function buttonsForImage(fullsrc, src, metadata) {
let isDataImage = src.startsWith('data:');
let mediaType = getMediaType(src);
buttons = [];
if (permissions.hasPermission('user_star_images') && !isDataImage) {
buttons.push({
Expand Down Expand Up @@ -139,6 +140,15 @@ function buttonsForImage(fullsrc, src, metadata) {
}
});
}
for (let reg of registeredMediaButtons) {
if (reg.showInHistory && (!reg.mediaTypes || reg.mediaTypes.includes(mediaType))) {
buttons.push({
label: reg.name,
title: reg.title,
onclick: () => reg.action(src)
});
}
}
return buttons;
}

Expand Down
11 changes: 11 additions & 0 deletions src/wwwroot/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,17 @@ function isAudioExt(filename) {
return false;
}

/** Returns 'video', 'audio', or 'image' based on the file source. */
function getMediaType(src) {
if (isVideoExt(src)) {
return 'video';
}
if (isAudioExt(src)) {
return 'audio';
}
return 'image';
}

/** 'string.split' with a count limit, and without the stupid misbehavior of the default JS 'string.split'. */
function splitWithTail(str, splitter, limit) {
let parts = str.split(splitter);
Expand Down
Loading