diff --git a/css/icons.css b/css/icons.css index a3b8ca9..e808b3b 100644 --- a/css/icons.css +++ b/css/icons.css @@ -1,3 +1,34 @@ +.ui.inline-icon { + position: relative; + + display: flex; +} + +.ui.inline-icon::after { + content: ""; + display: block; + + position: absolute; + + box-sizing: border-box; + + margin: auto; + top: 15%; + bottom: 15%; + + mask-size: contain; + mask-repeat: no-repeat; + + max-height: 70%; + aspect-ratio: 1; + + left: 0; + right: 0; + + background-color: var(--c-text); +} + +.ui.inline-icon.icon-eye-off::after, .ui.icon > .icon-eye-off { -webkit-mask-image: url("../res/icons/eye-off.svg"); mask-image: url("../res/icons/eye-off.svg"); @@ -42,3 +73,57 @@ -webkit-mask-image: url("../res/icons/settings.svg"); mask-image: url("../res/icons/settings.svg"); } + +.ui.inline-icon.icon-grid::after, +.ui.icon > .icon-grid { + -webkit-mask-image: url("../res/icons/grid.svg"); + mask-image: url("../res/icons/grid.svg"); +} + +.ui.inline-icon.icon-venetian-mask::after, +.ui.icon > .icon-venetian-mask { + -webkit-mask-image: url("../res/icons/venetian-mask.svg"); + mask-image: url("../res/icons/venetian-mask.svg"); +} + +.ui.inline-icon.icon-brush::after, +.ui.icon > .icon-brush { + -webkit-mask-image: url("../res/icons/brush.svg"); + mask-image: url("../res/icons/brush.svg"); +} + +.ui.inline-icon.icon-paintbrush::after, +.ui.icon > .icon-paintbrush { + -webkit-mask-image: url("../res/icons/paintbrush.svg"); + mask-image: url("../res/icons/paintbrush.svg"); +} + +.ui.inline-icon.icon-expand::after, +.ui.icon > .icon-expand { + -webkit-mask-image: url("../res/icons/expand.svg"); + mask-image: url("../res/icons/expand.svg"); +} + +.ui.inline-icon.icon-pin::after, +.ui.icon > .icon-pin { + -webkit-mask-image: url("../res/icons/pin.svg"); + mask-image: url("../res/icons/pin.svg"); +} + +.ui.inline-icon.icon-box-select::after, +.ui.icon > .icon-box-select { + -webkit-mask-image: url("../res/icons/box-select.svg"); + mask-image: url("../res/icons/box-select.svg"); +} + +.ui.inline-icon.icon-maximize::after, +.ui.icon > .icon-maximize { + -webkit-mask-image: url("../res/icons/maximize.svg"); + mask-image: url("../res/icons/maximize.svg"); +} + +.ui.inline-icon.icon-clipboard-list::after, +.ui.icon > .icon-clipboard-list { + -webkit-mask-image: url("../res/icons/clipboard-list.svg"); + mask-image: url("../res/icons/clipboard-list.svg"); +} diff --git a/css/ui/generic.css b/css/ui/generic.css index 93caf35..801e2ba 100644 --- a/css/ui/generic.css +++ b/css/ui/generic.css @@ -37,6 +37,8 @@ /* Slider Input */ div.slider-wrapper { margin: 5px; + margin-left: 0; + margin-right: 0; } div.slider-wrapper { @@ -100,6 +102,83 @@ div.slider-wrapper > input.text { background-color: transparent; } +/* Checkbox Input */ +div.checkbox-array { + display: flex; + + margin-top: 5px; + margin-bottom: 5px; +} + +input.oo-checkbox[type="checkbox"] { + /* Hide original checkbox */ + -webkit-appearance: none; + appearance: none; + + flex: 1; + + margin: 0; + + min-width: 28px; + height: 28px; + + background-color: var(--c-primary); + + cursor: pointer; +} + +input.oo-checkbox[type="checkbox"]:disabled { + background-color: #666 !important; + cursor: default !important; +} + +input.oo-checkbox[type="checkbox"]:disabled:hover { + filter: none !important; +} + +input.oo-checkbox[type="checkbox"]:checked::after { + background-color: #66f; +} + +input.oo-checkbox[type="checkbox"]:hover { + background-color: var(--c-hover); +} + +input.oo-checkbox[type="checkbox"]:active { + filter: brightness(120%); +} + +input.oo-checkbox[type="checkbox"]:first-child { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} + +input.oo-checkbox[type="checkbox"]:last-child { + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; +} + +/* Mask Inversion Checkbox */ +input.oo-checkbox[type="checkbox"].invert-mask-checkbox::after { + background-color: var(--c-text); +} + +input.oo-checkbox[type="checkbox"].invert-mask-checkbox:hover { + filter: brightness(120%); +} + +input.oo-checkbox[type="checkbox"].invert-mask-checkbox:active { + filter: brightness(140%); +} + +input.oo-checkbox[type="checkbox"].invert-mask-checkbox { + background-color: #922; +} + +input.oo-checkbox[type="checkbox"].invert-mask-checkbox:checked { + background-color: #229; +} + /* Bare Select */ .bareselector { diff --git a/index.html b/index.html index 25a4e67..6bd3985 100644 --- a/index.html +++ b/index.html @@ -5,12 +5,12 @@ openOutpaint 🐠 - + - + @@ -98,9 +98,12 @@ step="1" />
- +
-
+
+ +
+
+ + +
+ +
+ + - - - -
- -
- - +
@@ -187,7 +190,7 @@
- Alpha release v0.0.12.3 + Alpha release v0.0.12.5
@@ -311,12 +314,12 @@
+ src="pages/configuration.html?v=3d710ce">
- + @@ -325,7 +328,7 @@ - + - + - + diff --git a/js/global.js b/js/global.js index 19a3542..72cb565 100644 --- a/js/global.js +++ b/js/global.js @@ -3,6 +3,11 @@ */ const global = { + // If this is the first run of openOutpaint + get firstRun() { + return this._firstRun; + }, + // Connection _connection: "offline", set connection(v) { @@ -46,3 +51,5 @@ const global = { this.debug = !this.debug; }, }; + +global._firstRun = !localStorage.getItem("openoutpaint/host"); diff --git a/js/index.js b/js/index.js index fb68673..dabd70e 100644 --- a/js/index.js +++ b/js/index.js @@ -103,11 +103,13 @@ var stableDiffusionData = { mask: "", init_images: [], inpaint_full_res: false, - inpainting_fill: 2, + inpainting_fill: 1, enable_hr: false, restore_faces: false, - firstphase_width: 0, - firstphase_height: 0, + //firstphase_width: 0, + //firstphase_height: 0, //20230102 welp looks like the entire way HRfix is implemented has become bonkersly different + hr_scale: 2.0, + hr_upscaler: "None", styles: [], // here's some more fields that might be useful @@ -137,7 +139,6 @@ var host = ""; var url = "/sdapi/v1/"; const basePixelCount = 64; //64 px - ALWAYS 64 PX -// function startup() { testHostConfiguration(); loadSettings(); @@ -341,6 +342,12 @@ async function testHostConnection() { const response = await fetch( document.getElementById("host").value + "/sdapi/v1/options" ); + const optionsdata = await response.json(); + if (optionsdata["use_scale_latent_for_hires_fix"]) { + const message = `You are using an outdated version of A1111 webUI.\nThe HRfix options will not work until you update to at least commit ef27a18\n(https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/ef27a18b6b7cb1a8eebdc9b2e88d25baf2c2414d)\nor newer.`; + console.error(message); + alert(message); + } switch (response.status) { case 200: { setConnectionStatus("online"); @@ -552,6 +559,15 @@ const upscalerAutoComplete = createAutoComplete( document.getElementById("upscaler-ac-select") ); +const hrFixUpscalerAutoComplete = createAutoComplete( + "HRfix Upscaler", + document.getElementById("hrFixUpscaler") +); +hrFixUpscalerAutoComplete.onchange.on(({value}) => { + stableDiffusionData.hr_upscaler = value; + localStorage.setItem(`openoutpaint/hr_upscaler`, value); +}); + const resSlider = makeSlider( "Resolution", document.getElementById("resolution"), @@ -563,8 +579,6 @@ const resSlider = makeSlider( 2, (v) => { stableDiffusionData.width = stableDiffusionData.height = v; - stableDiffusionData.firstphase_width = - stableDiffusionData.firstphase_height = v / 2; toolbar.currentTool && toolbar.currentTool.redraw && @@ -621,15 +635,27 @@ makeSlider( 1 ); +// 20230102 grumble grumble makeSlider( - "HRfix Lock Px.", - document.getElementById("hrFixLock"), - "hr_fix_lock_px", + "HRfix Scale", + document.getElementById("hrFixScale"), + "hr_scale", + 1.0, + 4.0, + 0.1, + 2.0, + 0.1 +); + +makeSlider( + "HRfix Denoising", + document.getElementById("hrDenoising"), + "hr_denoising_strength", 0.0, - 768.0, - 256.0, - 0.0, - 1.0 + 1.0, + 0.05, + 0.7, + 0.01 ); function changeMaskBlur() { @@ -649,6 +675,23 @@ function changeHiResFix() { document.getElementById("cbxHRFix").checked ); localStorage.setItem("openoutpaint/enable_hr", stableDiffusionData.enable_hr); + var hrfSlider = document.getElementById("hrFixScale"); + var hrfOpotions = document.getElementById("hrFixUpscaler"); + var hrfLabel = document.getElementById("hrFixLabel"); + var hrfDenoiseSlider = document.getElementById("hrDenoising"); + if (stableDiffusionData.enable_hr) { + hrfSlider.classList.remove("invisible"); + hrfOpotions.classList.remove("invisible"); + hrfLabel.classList.remove("invisible"); + hrfDenoiseSlider.classList.remove("invisible"); + //state.ctxmenu.keepUnmaskedBlurSliderLinebreak.classList.add("invisible"); + } else { + hrfSlider.classList.add("invisible"); + hrfOpotions.classList.add("invisible"); + hrfLabel.classList.add("invisible"); + hrfDenoiseSlider.classList.add("invisible"); + //state.ctxmenu.keepUnmaskedBlurSliderLinebreak.classList.remove("invisible"); + } } function changeRestoreFaces() { @@ -743,17 +786,26 @@ async function getUpscalers() { "[index] purposefully_incorrect_data response, ignore above error" ); // result = purposefully_incorrect_data response: Invalid upscaler, needs to be on of these: None , Lanczos , Nearest , LDSR , BSRGAN , R-ESRGAN General 4xV3 , R-ESRGAN 4x+ Anime6B , ScuNET , ScuNET PSNR , SwinIR_4x - const upscalers = data.detail + const upscalersPlusNone = data.detail .split(": ")[1] .split(",") - .map((v) => v.trim()) - .filter((v) => v !== "None"); // converting the result to a list of upscalers + .map((v) => v.trim()); // need "None" for stupid hrfix changes razza frazza + const upscalers = upscalersPlusNone.filter((v) => v !== "None"); // converting the result to a list of upscalers + upscalersPlusNone.push("Latent"); + upscalersPlusNone.push("Latent (nearest)"); // GRUMBLE GRUMBLE upscalerAutoComplete.options = upscalers.map((u) => { return {name: u, value: u}; }); + hrFixUpscalerAutoComplete.options = upscalersPlusNone.map((u) => { + return {name: u, value: u}; + }); upscalerAutoComplete.value = upscalers[0]; + hrFixUpscalerAutoComplete.value = + localStorage.getItem("openoutpaint/hr_upscaler") === null + ? "None" + : localStorage.getItem("openoutpaint/hr_upscaler"); } catch (e) { console.warn("[index] Failed to fetch upscalers:"); console.warn(e); @@ -804,12 +856,14 @@ async function getUpscalers() { } async function getModels() { - var url = document.getElementById("host").value + "/sdapi/v1/sd-models"; + const url = document.getElementById("host").value + "/sdapi/v1/sd-models"; + let opt = null; + try { const response = await fetch(url); const data = await response.json(); - modelAutoComplete.options = data.map((option) => ({ + opt = data.map((option) => ({ name: option.title, value: option.title, optionelcb: (el) => { @@ -818,6 +872,8 @@ async function getModels() { }, })); + modelAutoComplete.options = opt; + try { const optResponse = await fetch( document.getElementById("host").value + "/sdapi/v1/options" @@ -838,10 +894,10 @@ async function getModels() { modelAutoComplete.onchange.on(async ({value}) => { console.log(`[index] Changing model to [${value}]`); - var payload = { + const payload = { sd_model_checkpoint: value, }; - var url = document.getElementById("host").value + "/sdapi/v1/options/"; + const url = document.getElementById("host").value + "/sdapi/v1/options/"; try { await fetch(url, { method: "POST", @@ -861,6 +917,25 @@ async function getModels() { ); } }); + + // If first time running, ask if user wants to switch to an inpainting model + if (global.firstRun && !modelAutoComplete.value.includes("inpainting")) { + const inpainting = opt.find(({name}) => name.includes("inpainting")); + + let message = + "It seems this is your first time using openOutpaint. It is highly recommended that you switch to an inpainting model. \ + These are highlighted as green in the model selector."; + + if (inpainting) { + message += `\n\nWe have found the inpainting model\n\n - ${inpainting.name}\n\navailable in the webui. Do you want to switch to it?`; + if (confirm(message)) { + modelAutoComplete.value = inpainting.value; + } + } else { + message += `\n\nNo inpainting model seems to be available in the webui. It is recommended that you download an inpainting model, or outpainting results may not be optimal.`; + alert(message); + } + } } async function getConfig() { @@ -1060,6 +1135,15 @@ function loadSettings() { ? true : localStorage.getItem("openoutpaint/sync_cursor_size") === "true"; + let _hrfix_scale = + localStorage.getItem("openoutpaint/hr_scale") === null + ? 2.0 + : localStorage.getItem("openoutpaint/hr_scale"); + + let _hrfix_denoising = + localStorage.getItem("openoutpaint/hr_denoising_strength") === null + ? 0.7 + : localStorage.getItem("openoutpaint/hr_denoising_strength"); // set the values into the UI document.getElementById("maskBlur").value = Number(_mask_blur); document.getElementById("seed").value = Number(_seed); @@ -1067,6 +1151,8 @@ function loadSettings() { document.getElementById("cbxRestoreFaces").checked = Boolean(_restore_faces); document.getElementById("cbxSyncCursorSize").checked = Boolean(_sync_cursor_size); + document.getElementById("hrFixScale").value = Number(_hrfix_scale); + document.getElementById("hrDenoising").value = Number(_hrfix_denoising); } imageCollection.element.addEventListener( diff --git a/js/lib/toolbar.js b/js/lib/toolbar.js index 232b0fe..10e48f8 100644 --- a/js/lib/toolbar.js +++ b/js/lib/toolbar.js @@ -145,17 +145,24 @@ const toolbar = { * Premade inputs for populating the context menus */ const _toolbar_input = { - checkbox: (state, dataKey, text, cb = null) => { + checkbox: (state, dataKey, text, classes, cb = null) => { if (state[dataKey] === undefined) state[dataKey] = false; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; + checkbox.classList.add("oo-checkbox", "ui", "inline-icon"); + + if (typeof classes === "string") classes = [classes]; + + if (classes) checkbox.classList.add(...classes); checkbox.checked = state[dataKey]; checkbox.onchange = () => { state[dataKey] = checkbox.checked; cb && cb(); }; + checkbox.title = text; + const label = document.createElement("label"); label.appendChild(checkbox); label.appendChild(new Text(text)); diff --git a/js/ui/tool/colorbrush.js b/js/ui/tool/colorbrush.js index 5786307..dc1d190 100644 --- a/js/ui/tool/colorbrush.js +++ b/js/ui/tool/colorbrush.js @@ -349,13 +349,16 @@ const colorBrushTool = () => state.ctxmenu = {}; // Affects Mask Checkbox + const array = document.createElement("div"); const affectMaskCheckbox = _toolbar_input.checkbox( state, "affectMask", - "Affect Mask" - ).label; + "Affect Mask", + "icon-venetian-mask" + ).checkbox; + array.appendChild(affectMaskCheckbox); - state.ctxmenu.affectMaskCheckbox = affectMaskCheckbox; + state.ctxmenu.affectMaskCheckbox = array; // Brush size slider const brushSizeSlider = _toolbar_input.slider( diff --git a/js/ui/tool/dream.js b/js/ui/tool/dream.js index 30e73fd..8d04257 100644 --- a/js/ui/tool/dream.js +++ b/js/ui/tool/dream.js @@ -130,6 +130,25 @@ const _dream = async (endpoint, request) => { let data = null; try { generating = true; + if ( + endpoint == "txt2img" && + request.enable_hr && + localStorage.getItem("openoutpaint/settings.hrfix-liar") == "true" + ) { + /** + * try and make the new HRfix method useful for our purposes + * since it now returns an image that's been upscaled x the hr_scale parameter, + * we cheekily lie to SD and tell it that the original dimensions are _divided_ + * by the scale factor so it returns something about the same size as we wanted initially + */ + var newWidth = Math.floor(request.width / request.hr_scale); + var newHeight = Math.floor(request.height / request.hr_scale); + request.width = newWidth; + request.height = newHeight; + } + if (endpoint == "txt2img") { + request.denoising_strength = stableDiffusionData.hr_denoising_strength; + } const response = await fetch(apiURL, { method: "POST", headers: { @@ -977,7 +996,7 @@ const dream_img2img_callback = (bb, resolution, state) => { request.height = resolution.h; request.denoising_strength = state.denoisingStrength; - request.inpainting_fill = state.inpainting_fill; //let's see how this works //1; // For img2img use original + request.inpainting_fill = state.inpainting_fill ?? 1; //let's see how this works //1; // For img2img use original // Load prompt (maybe we should add some events so we don't have to do this) request.prompt = document.getElementById("prompt").value; @@ -1392,18 +1411,6 @@ const dreamTool = () => h: stableDiffusionData.height, }; - //hacky set non-square auto hrfix values - let hrLockPx = - localStorage.getItem("openoutpaint/hr_fix_lock_px") ?? 0; - stableDiffusionData.firstphase_height = - hrLockPx == 0 || resolution.h / 2 <= hrLockPx - ? resolution.h / 2 - : hrLockPx; - stableDiffusionData.firstphase_width = - hrLockPx == 0 || resolution.w / 2 <= hrLockPx - ? resolution.w / 2 - : hrLockPx; - if (global.connection === "online") { dream_generate_callback(bb, resolution, state); } else { @@ -1474,34 +1481,43 @@ const dreamTool = () => state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( state, "snapToGrid", - "Snap To Grid" - ).label; + "Snap To Grid", + "icon-grid" + ).checkbox; // Invert Mask Checkbox state.ctxmenu.invertMaskLabel = _toolbar_input.checkbox( state, "invertMask", "Invert Mask", + ["icon-venetian-mask", "invert-mask-checkbox"], () => { setMask(state.invertMask ? "hold" : "clear"); } - ).label; + ).checkbox; // Keep Masked Content Checkbox state.ctxmenu.keepUnmaskedLabel = _toolbar_input.checkbox( state, "keepUnmasked", "Keep Unmasked", + "icon-pin", () => { if (state.keepUnmasked) { state.ctxmenu.keepUnmaskedBlurSlider.classList.remove( "invisible" ); + state.ctxmenu.keepUnmaskedBlurSliderLinebreak.classList.add( + "invisible" + ); } else { state.ctxmenu.keepUnmaskedBlurSlider.classList.add("invisible"); + state.ctxmenu.keepUnmaskedBlurSliderLinebreak.classList.remove( + "invisible" + ); } } - ).label; + ).checkbox; // Keep Masked Content Blur Slider state.ctxmenu.keepUnmaskedBlurSlider = _toolbar_input.slider( @@ -1516,12 +1532,19 @@ const dreamTool = () => } ).slider; + state.ctxmenu.keepUnmaskedBlurSliderLinebreak = + document.createElement("br"); + state.ctxmenu.keepUnmaskedBlurSliderLinebreak.classList.add( + "invisible" + ); + // Preserve Brushed Masks Checkbox state.ctxmenu.preserveMasksLabel = _toolbar_input.checkbox( state, "preserveMasks", - "Preserve Brushed Masks" - ).label; + "Preserve Brushed Masks", + "icon-paintbrush" + ).checkbox; // Overmasking Slider state.ctxmenu.overMaskPxLabel = _toolbar_input.slider( @@ -1551,14 +1574,19 @@ const dreamTool = () => } menu.appendChild(state.ctxmenu.cursorSizeSlider); - menu.appendChild(state.ctxmenu.snapToGridLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.invertMaskLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.keepUnmaskedLabel); + const array = document.createElement("div"); + array.classList.add("checkbox-array"); + array.appendChild(state.ctxmenu.snapToGridLabel); + //menu.appendChild(document.createElement("br")); + array.appendChild(state.ctxmenu.invertMaskLabel); + array.appendChild(state.ctxmenu.preserveMasksLabel); + //menu.appendChild(document.createElement("br")); + array.appendChild(state.ctxmenu.keepUnmaskedLabel); + menu.appendChild(array); menu.appendChild(state.ctxmenu.keepUnmaskedBlurSlider); - menu.appendChild(state.ctxmenu.preserveMasksLabel); - menu.appendChild(document.createElement("br")); + // menu.appendChild(state.ctxmenu.keepUnmaskedBlurSliderLinebreak); + // menu.appendChild(state.ctxmenu.preserveMasksLabel); + // menu.appendChild(document.createElement("br")); menu.appendChild(state.ctxmenu.overMaskPxLabel); menu.appendChild(state.ctxmenu.eagerGenerateCountLabel); }, @@ -1979,24 +2007,27 @@ const img2imgTool = () => state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( state, "snapToGrid", - "Snap To Grid" - ).label; + "Snap To Grid", + "icon-grid" + ).checkbox; // Invert Mask Checkbox state.ctxmenu.invertMaskLabel = _toolbar_input.checkbox( state, "invertMask", "Invert Mask", + ["icon-venetian-mask", "invert-mask-checkbox"], () => { setMask(state.invertMask ? "hold" : "clear"); } - ).label; + ).checkbox; // Keep Masked Content Checkbox state.ctxmenu.keepUnmaskedLabel = _toolbar_input.checkbox( state, "keepUnmasked", "Keep Unmasked", + "icon-pin", () => { if (state.keepUnmasked) { state.ctxmenu.keepUnmaskedBlurSlider.classList.remove( @@ -2012,7 +2043,7 @@ const img2imgTool = () => ); } } - ).label; + ).checkbox; // Keep Masked Content Blur Slider state.ctxmenu.keepUnmaskedBlurSlider = _toolbar_input.slider( @@ -2037,15 +2068,17 @@ const img2imgTool = () => state.ctxmenu.preserveMasksLabel = _toolbar_input.checkbox( state, "preserveMasks", - "Preserve Brushed Masks" - ).label; + "Preserve Brushed Masks", + "icon-paintbrush" + ).checkbox; // Inpaint Full Resolution Checkbox state.ctxmenu.fullResolutionLabel = _toolbar_input.checkbox( state, "fullResolution", - "Inpaint Full Resolution" - ).label; + "Inpaint Full Resolution", + "icon-expand" + ).checkbox; // Denoising Strength Slider state.ctxmenu.denoisingStrengthSlider = _toolbar_input.slider( @@ -2064,8 +2097,9 @@ const img2imgTool = () => state.ctxmenu.borderMaskGradientCheckbox = _toolbar_input.checkbox( state, "gradient", - "Border Mask Gradient" - ).label; + "Border Mask Gradient", + "icon-box-select" + ).checkbox; // Border Mask Size Slider state.ctxmenu.borderMaskSlider = _toolbar_input.slider( @@ -2112,20 +2146,22 @@ const img2imgTool = () => } menu.appendChild(state.ctxmenu.cursorSizeSlider); - menu.appendChild(state.ctxmenu.snapToGridLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.invertMaskLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.keepUnmaskedLabel); + const array = document.createElement("div"); + array.classList.add("checkbox-array"); + array.appendChild(state.ctxmenu.snapToGridLabel); + array.appendChild(state.ctxmenu.invertMaskLabel); + array.appendChild(state.ctxmenu.preserveMasksLabel); + array.appendChild(state.ctxmenu.keepUnmaskedLabel); + menu.appendChild(array); menu.appendChild(state.ctxmenu.keepUnmaskedBlurSlider); - menu.appendChild(state.ctxmenu.keepUnmaskedBlurSliderLinebreak); - menu.appendChild(state.ctxmenu.preserveMasksLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.fullResolutionLabel); - menu.appendChild(document.createElement("br")); + // menu.appendChild(state.ctxmenu.keepUnmaskedBlurSliderLinebreak); menu.appendChild(state.ctxmenu.inpaintTypeSelect); menu.appendChild(state.ctxmenu.denoisingStrengthSlider); - menu.appendChild(state.ctxmenu.borderMaskGradientCheckbox); + const btnArray2 = document.createElement("div"); + btnArray2.classList.add("checkbox-array"); + btnArray2.appendChild(state.ctxmenu.fullResolutionLabel); + btnArray2.appendChild(state.ctxmenu.borderMaskGradientCheckbox); + menu.appendChild(btnArray2); menu.appendChild(state.ctxmenu.borderMaskSlider); menu.appendChild(state.ctxmenu.eagerGenerateCountLabel); }, diff --git a/js/ui/tool/interrogate.js b/js/ui/tool/interrogate.js index 2090c03..b7fc403 100644 --- a/js/ui/tool/interrogate.js +++ b/js/ui/tool/interrogate.js @@ -97,8 +97,9 @@ const interrogateTool = () => state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( state, "snapToGrid", - "Snap To Grid" - ).label; + "Snap To Grid", + "icon-grid" + ).checkbox; } menu.appendChild(state.ctxmenu.cursorSizeSlider); diff --git a/js/ui/tool/select.js b/js/ui/tool/select.js index c62cf14..11eb700 100644 --- a/js/ui/tool/select.js +++ b/js/ui/tool/select.js @@ -633,23 +633,26 @@ const selectTransformTool = () => state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( state, "snapToGrid", - "Snap To Grid" - ).label; + "Snap To Grid", + "icon-grid" + ).checkbox; // Keep Aspect Ratio state.ctxmenu.keepAspectRatioLabel = _toolbar_input.checkbox( state, "keepAspectRatio", - "Keep Aspect Ratio" - ).label; + "Keep Aspect Ratio", + "icon-maximize" + ).checkbox; // Use Clipboard const clipboardCheckbox = _toolbar_input.checkbox( state, "useClipboard", - "Use clipboard" + "Use clipboard", + "icon-clipboard-list" ); - state.ctxmenu.useClipboardLabel = clipboardCheckbox.label; + state.ctxmenu.useClipboardLabel = clipboardCheckbox.checkbox; if (!(navigator.clipboard && navigator.clipboard.write)) clipboardCheckbox.checkbox.disabled = true; // Disable if not available @@ -760,11 +763,12 @@ const selectTransformTool = () => state.ctxmenu.actionArray = actionArray; state.ctxmenu.visibleActionArray = visibleActionArray; } - menu.appendChild(state.ctxmenu.snapToGridLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.keepAspectRatioLabel); - menu.appendChild(document.createElement("br")); - menu.appendChild(state.ctxmenu.useClipboardLabel); + const array = document.createElement("div"); + array.classList.add("checkbox-array"); + array.appendChild(state.ctxmenu.snapToGridLabel); + array.appendChild(state.ctxmenu.keepAspectRatioLabel); + array.appendChild(state.ctxmenu.useClipboardLabel); + menu.appendChild(array); menu.appendChild(state.ctxmenu.selectionPeekOpacitySlider); menu.appendChild(state.ctxmenu.actionArray); menu.appendChild(state.ctxmenu.visibleActionArray); diff --git a/js/ui/tool/stamp.js b/js/ui/tool/stamp.js index 74be4ad..fc1cb89 100644 --- a/js/ui/tool/stamp.js +++ b/js/ui/tool/stamp.js @@ -368,11 +368,17 @@ const stampTool = () => if (!state.ctxmenu) { state.ctxmenu = {}; // Snap To Grid Checkbox - state.ctxmenu.snapToGridLabel = _toolbar_input.checkbox( - state, - "snapToGrid", - "Snap To Grid" - ).label; + const array = document.createElement("div"); + array.classList.add("checkbox-array"); + array.appendChild( + _toolbar_input.checkbox( + state, + "snapToGrid", + "Snap To Grid", + "icon-grid" + ).checkbox + ); + state.ctxmenu.snapToGridLabel = array; // Create resource list const uploadButtonId = `upload-btn-${guid()}`; diff --git a/pages/configuration.html b/pages/configuration.html index 2adff2a..5e53994 100644 --- a/pages/configuration.html +++ b/pages/configuration.html @@ -5,12 +5,12 @@ openOutpaint 🐠 - + - + @@ -84,6 +84,10 @@ step="0.1" value="30.0" /> +

Refresh the page to apply settings.

diff --git a/res/icons/clipboard-list.svg b/res/icons/clipboard-list.svg new file mode 100644 index 0000000..45bbcf5 --- /dev/null +++ b/res/icons/clipboard-list.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/icons/equal.svg b/res/icons/equal.svg new file mode 100644 index 0000000..6b5c5c8 --- /dev/null +++ b/res/icons/equal.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/res/icons/expand.svg b/res/icons/expand.svg new file mode 100644 index 0000000..6f5864a --- /dev/null +++ b/res/icons/expand.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/res/icons/grid.svg b/res/icons/grid.svg new file mode 100644 index 0000000..a7daf6d --- /dev/null +++ b/res/icons/grid.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/res/icons/maximize.svg b/res/icons/maximize.svg new file mode 100644 index 0000000..5c8a6e3 --- /dev/null +++ b/res/icons/maximize.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/res/icons/pin.svg b/res/icons/pin.svg new file mode 100644 index 0000000..aa858be --- /dev/null +++ b/res/icons/pin.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/res/icons/square.svg b/res/icons/square.svg new file mode 100644 index 0000000..ba8d095 --- /dev/null +++ b/res/icons/square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/res/icons/venetian-mask.svg b/res/icons/venetian-mask.svg new file mode 100644 index 0000000..6cd8962 --- /dev/null +++ b/res/icons/venetian-mask.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file