├── .gitignore ├── LICENSE ├── README.md ├── imgs ├── 1.png ├── 2.png └── 3.png ├── javascript └── split-layout.js ├── scripts ├── split-layout.py └── val.txt └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | /cache/* 2 | __pycache__ 3 | *.git* 4 | *.swp 5 | *.pyc 6 | *.idea* 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Six_GodK 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # sd-webui-split-layout 3 | 4 | ## 针对sd-webui左右布局调整大小,习惯原始风格界面的可以试试 5 | 6 | #### for those who prefer the original style interface, you can try adjusting the size of the layout using this extension 7 | 8 | ### 原始界面 9 | ![Image text](imgs/1.png) 10 | ### 扩展界面 11 | ![Image text](imgs/2.png) 12 | ### 修改后界面 13 | ![Image text](imgs/3.png) 14 | -------------------------------------------------------------------------------- /imgs/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisjam/sd-webui-split-layout/bf838e8541055e1c05f1085f9c1a3cafadf44eef/imgs/1.png -------------------------------------------------------------------------------- /imgs/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisjam/sd-webui-split-layout/bf838e8541055e1c05f1085f9c1a3cafadf44eef/imgs/2.png -------------------------------------------------------------------------------- /imgs/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisjam/sd-webui-split-layout/bf838e8541055e1c05f1085f9c1a3cafadf44eef/imgs/3.png -------------------------------------------------------------------------------- /javascript/split-layout.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: SixGod_K 3 | * @Date: 2023-08-30 16:47:14 4 | * @LastEditors: Six_God_K 5 | * @LastEditTime: 2024-05-03 14:53:10 6 | * @FilePath: \sd-webui-split-layout\javascript\split-layout.js 7 | * @Description: 8 | * 9 | */ 10 | 11 | class LayoutSplit { 12 | constructor() { 13 | this.Doms = null 14 | 15 | }; 16 | loadNode(callBack) { 17 | let doms = { 18 | tab: getEle('#tab_split-layout'), 19 | txtleft: getEle('#txt2img_settings'), 20 | imgleft: getEle('#img2img_settings'), 21 | btnSave: getEle('#sixgod-layout-save'), 22 | sliderInput: getEle("#sixgod-layout-slider input"), 23 | sliderInput2: getEle("#sixgod-layout-slider2 input"), 24 | txt2imgarea: getEle('#txt2img_gallery'), 25 | img2imgarea: getEle('#img2img_gallery'), 26 | 27 | genaTxtbtn: getEle('#txt2img_generate_box'), 28 | genaImgbtn: getEle('#img2img_generate_box'), 29 | txt2imgTools: getEle('#txt2img_tools'), 30 | img2imgTools: getEle('#img2img_tools'), 31 | txtPreview: getEle('#image_buttons_txt2img'), 32 | imgPreview: getEle('#image_buttons_img2img'), 33 | 34 | txt2img_extra_tabs: getEle('#txt2img_extra_tabs'), 35 | img2img_extra_tabs: getEle('#img2img_extra_tabs'), 36 | clone1: null, 37 | clone2: null, 38 | } 39 | this.Doms = doms 40 | callBack && callBack() 41 | 42 | }; 43 | 44 | 45 | 46 | moveData() { 47 | let prompt1 = getEle('#oldsix-prompt1') 48 | let prompt2 = getEle('#oldsix-prompt2') 49 | 50 | if (!prompt1 || !prompt2) return 51 | this.Doms.txtleft.insertAdjacentElement('afterbegin', prompt1) 52 | this.Doms.imgleft.insertAdjacentElement('afterbegin', prompt2) 53 | 54 | }; 55 | 56 | 57 | 58 | 59 | addSaveSlideEve() { 60 | // let widthval = getEle('#sixgod-layout-width').innerHTML; 61 | // let heightval = getEle('#sixgod-layout-height').innerHTML; 62 | // console.log(widthval, heightval); 63 | // this.setWidthValue(widthval, heightval); 64 | // this.Doms.btnSave.onclick = () => { 65 | // this.setWidthValue(this.Doms.sliderInput.value, this.Doms.sliderInput2.value) 66 | // }; 67 | 68 | let div = document.createElement('div') 69 | div.classList.add("sixgod-slider-layout-container") 70 | let inputWitdh=this.createInputRange(true,div) 71 | let inputHeight=this.createInputRange(false,div) 72 | 73 | let p = document.createElement('p') 74 | let btn = document.createElement('button') 75 | btn.innerHTML='保存' 76 | 77 | btn.addEventListener('click', () => { 78 | localStorage.setItem('sixgod-slider-layout',JSON.stringify([inputWitdh.value,inputHeight.value])) 79 | this.setWidthValue(inputWitdh.value, inputHeight.value) 80 | }) 81 | this.setWidthValue(inputWitdh.value, inputHeight.value) 82 | 83 | div.appendChild(p) 84 | p.appendChild(btn) 85 | this.Doms.tab.appendChild(div) 86 | 87 | 88 | }; 89 | 90 | createInputRange(iswidth,parentDom){ 91 | let p = document.createElement('p') 92 | let labelTitle = document.createElement('label') 93 | labelTitle.innerHTML=iswidth?'宽度:':'高度:'; 94 | let input = document.createElement('input') 95 | input.type = 'range' 96 | input.min = 100 97 | input.max = iswidth?1600:800; 98 | let cache=localStorage.getItem('sixgod-slider-layout') 99 | if(cache){ 100 | input.value=JSON.parse(cache)[iswidth?0:1] 101 | } 102 | else{ 103 | input.value=input.max 104 | localStorage.setItem('sixgod-slider-layout',JSON.stringify([1600,800])) 105 | } 106 | let labelNum = document.createElement('label') 107 | labelNum.innerHTML=input.value 108 | p.appendChild(labelTitle) 109 | p.appendChild(input) 110 | p.appendChild(labelNum) 111 | parentDom.appendChild(p) 112 | input.addEventListener('input', function() { 113 | labelNum.innerHTML= input.value ; 114 | }); 115 | return input 116 | } 117 | 118 | 119 | 120 | setWidthValue(widthval, heightval) { 121 | this.Doms.txtleft.parentNode.style.gridTemplateColumns = `${widthval}px 16px 1fr`; 122 | this.Doms.imgleft.parentNode.style.gridTemplateColumns = `${widthval}px 16px 1fr`; 123 | this.Doms.txt2imgarea.style.height = `${heightval}px`; 124 | this.Doms.img2imgarea.style.height = `${heightval}px`; 125 | 126 | }; 127 | 128 | moveRightMenu(eve) { 129 | 130 | setTimeout(() => { 131 | getEle('#context-menu').style.top = eve.pageY + 'px' 132 | getEle('#context-menu').style.left = eve.pageX + 'px' 133 | }, 50); 134 | 135 | 136 | } 137 | 138 | onScrollevent(top = 220) { 139 | this.Doms.clone1 = this.Doms.genaTxtbtn.cloneNode(true)//div 140 | this.Doms.clone2 = this.Doms.genaImgbtn.cloneNode(true)//div 141 | 142 | let btn_txt2img_generate=this.Doms.clone1.querySelector('#txt2img_generate') 143 | let btn_img2img_generate=this.Doms.clone2.querySelector('#img2img_generate') 144 | 145 | btn_txt2img_generate.classList.add('secondary') 146 | btn_img2img_generate.classList.add('secondary') 147 | btn_txt2img_generate.disabled = true; 148 | btn_img2img_generate.disabled = true; 149 | 150 | 151 | this.Doms.txtPreview.after(this.Doms.clone1) 152 | this.Doms.imgPreview.after(this.Doms.clone2) 153 | 154 | this.Doms.genaTxtbtn.addEventListener('contextmenu', (event) => { 155 | 156 | this.moveRightMenu(event) 157 | }) 158 | this.Doms.genaImgbtn.addEventListener('contextmenu', (event) => { 159 | 160 | this.moveRightMenu(event) 161 | }) 162 | let btnRect= this.Doms.genaTxtbtn.getBoundingClientRect(); 163 | 164 | top=btnRect.top+btnRect.height 165 | 166 | window.addEventListener('scroll', () => { 167 | let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; 168 | 169 | if (scrollTop > top) { 170 | //超过 171 | this.Doms.clone1.classList.add('sixgod-hide') 172 | this.Doms.clone2.classList.add('sixgod-hide') 173 | this.Doms.txtPreview.after(this.Doms.genaTxtbtn) 174 | this.Doms.imgPreview.after(this.Doms.genaImgbtn) 175 | 176 | } 177 | else if (scrollTop < top) { 178 | this.Doms.clone1.classList.remove('sixgod-hide') 179 | this.Doms.clone2.classList.remove('sixgod-hide') 180 | this.Doms.txt2imgTools.before(this.Doms.genaTxtbtn) 181 | this.Doms.img2imgTools.before(this.Doms.genaImgbtn) 182 | } 183 | }); 184 | } 185 | 186 | 187 | 188 | 189 | 190 | processTabsBtn(domstabs, parentId) { 191 | let tabs = domstabs.children[0];//第一个div tabs 192 | let generantion = getEle(parentId).querySelector('.sixgod-rightslid-generantion') 193 | let buttons = tabs.querySelectorAll(':scope>button'); 194 | StyleObserver(generantion, 'none', 'block') 195 | this.btnsEvent(domstabs) 196 | buttons[3].click() 197 | } 198 | 199 | btnsEvent(domstabs) { 200 | let tabs = domstabs.children[0];//第一个div tabs 201 | let textinput = tabs.querySelector('input');//第一个div tabs 202 | let buttons = tabs.querySelectorAll(':scope>button'); 203 | buttons.forEach(btn => { 204 | btn.onclick = (eve) => { 205 | this.btnsEvent(domstabs) 206 | // textinput.value = '' 207 | // textinput&&updateInput(textinput) 208 | buttons[0].classList.add("sixgod-hide") 209 | 210 | } 211 | }) 212 | for (let index = 0; index < 100; index++) { 213 | setTimeout(() => { 214 | tabs.children[0].classList.add("sixgod-hide") 215 | }, 10); 216 | 217 | } 218 | 219 | } 220 | 221 | 222 | processSildeLayout(domstabs, parentId) { 223 | 224 | //domstabs=txt2img_extra_tabs||img2img_extra_tabs 225 | let silder = document.createElement('div') 226 | silder.classList.add("sixgod-silder") 227 | 228 | //创建右侧悬浮窗 229 | let rightslid = document.createElement('div') 230 | rightslid.classList.add("sixgod-rightslid") 231 | silder.appendChild(rightslid) 232 | rightslid.appendChild(domstabs) 233 | 234 | //参数面板 235 | let generantion = domstabs.children[1]; 236 | 237 | generantion.className = "sixgod-rightslid-generantion" 238 | getEle(parentId).appendChild(silder) 239 | getEle(parentId).appendChild(generantion) 240 | 241 | this.processTabsBtn(domstabs, parentId) 242 | 243 | 244 | 245 | 246 | } 247 | 248 | //处理别的插件做兼容 loartool 249 | processLoraTool() { 250 | // 监视元素 251 | const loraTool = document.getElementById('lora-context-menu'); 252 | if (!loraTool) return 253 | const targetElement = document.getElementById('tab_txt2img'); 254 | 255 | this.Doms.txt2img_extra_tabs.after(loraTool) 256 | 257 | const observer = new MutationObserver((mutationsList, observer) => { 258 | for (let mutation of mutationsList) { 259 | if (mutation.type === 'attributes' && mutation.attributeName === 'style') { 260 | if (targetElement.style.display === 'none') { 261 | this.Doms.img2img_extra_tabs.after(loraTool) 262 | } 263 | else { 264 | this.Doms.txt2img_extra_tabs.after(loraTool) 265 | } 266 | 267 | } 268 | } 269 | }); 270 | 271 | // 以上面的配置初始化观察者 272 | observer.observe(targetElement, { attributes: true, attributeFilter: ['style'] }); 273 | 274 | } 275 | 276 | 277 | 278 | init() { 279 | this.loadNode(() => { 280 | this.moveData(); 281 | this.addSaveSlideEve(); 282 | this.onScrollevent(); 283 | this.processSildeLayout(this.Doms.txt2img_extra_tabs, '#tab_txt2img') 284 | this.processSildeLayout(this.Doms.img2img_extra_tabs, '#tab_img2img') 285 | this.processLoraTool() 286 | 287 | }) 288 | } 289 | 290 | 291 | } 292 | 293 | 294 | 295 | function getEle(key) { 296 | return gradioApp().querySelector(key) 297 | } 298 | function getEleAll(key) { 299 | return gradioApp().querySelectorAll(key) 300 | } 301 | 302 | 303 | function StyleObserver(element, statu1, statu2) { 304 | var observer = new MutationObserver(function (mutationsList) { 305 | mutationsList.forEach(function (mutation) { 306 | if (mutation.attributeName === 'style') { 307 | 308 | var display = window.getComputedStyle(element).display; 309 | if (display === statu1|| display == '') { 310 | element.style.display = statu2; 311 | } 312 | } 313 | }); 314 | }); 315 | 316 | // 配置要监听的 mutation 317 | var config = { attributes: true, attributeOldValue: true, attributeFilter: ['style'] }; 318 | 319 | // 开始监听 320 | observer.observe(element, config); 321 | 322 | } 323 | 324 | 325 | 326 | 327 | 328 | 329 | onUiLoaded(async () => { 330 | 331 | let layout = new LayoutSplit(); 332 | layout.init() 333 | 334 | 335 | }) -------------------------------------------------------------------------------- /scripts/split-layout.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Author: SixGod_K 3 | Date: 2023-08-30 23:05:20 4 | LastEditors: Six_God_K 5 | LastEditTime: 2024-05-03 13:32:01 6 | FilePath: \webui\extensions\sd-webui-split-layout\scripts\split-layout.py 7 | Description: 8 | 9 | ''' 10 | 11 | import gradio as gr 12 | from pathlib import Path 13 | from modules import shared, script_callbacks, scripts 14 | import json 15 | import os 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | def on_ui_tabs(): 24 | 25 | with gr.Blocks() as layout: 26 | 27 | pass 28 | return [ 29 | (layout, "split-layout", "split-layout"), 30 | ] 31 | # return [layout] 32 | 33 | 34 | 35 | 36 | script_callbacks.on_ui_tabs(on_ui_tabs) -------------------------------------------------------------------------------- /scripts/val.txt: -------------------------------------------------------------------------------- 1 | 949,488 -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #oldsix-prompt1,#oldsix-prompt2{ 6 | margin: 10px auto 10px auto !important; 7 | } 8 | 9 | 10 | 11 | #sixgod-layout-slider,#sixgod-layout-save,#sixgod-layout-slider2{ 12 | max-width: 40% !important; 13 | padding: 10px; 14 | margin: 20px auto; 15 | 16 | } 17 | 18 | .sixgod-hide{ 19 | display: none !important; 20 | } 21 | .progressDiv{ 22 | z-index: 1; 23 | top: -5px; 24 | } 25 | 26 | #txt2img_gallery_container{ 27 | position: relative; 28 | z-index: 0; 29 | } 30 | 31 | #img2img_results, #txt2img_results{ 32 | top: 1.5em; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | .sixgod-rightslid{ 40 | 41 | width: 30px !important; 42 | height: 100%; 43 | position: fixed; 44 | right: 0; 45 | top:10%; 46 | border-radius: 5px; 47 | background: var(--body-background-fill) ; 48 | transition: width 0.1s ease-in-out; 49 | z-index: 999; 50 | opacity:0; 51 | border: 1px solid #ccc; 52 | 53 | 54 | /* 55 | width: 600px !important; 56 | opacity:1; */ 57 | 58 | } 59 | 60 | .sixgod-rightslid:hover{ 61 | width: 600px !important; 62 | opacity:1; 63 | 64 | } 65 | .sixgod-rightslid:hover .tabs{ 66 | 67 | opacity:1; 68 | } 69 | 70 | .sixgod-rightslid .tabs{ 71 | 72 | opacity:0; 73 | /* opacity:1; */ 74 | } 75 | 76 | .card{ 77 | 78 | margin: 0.2rem!important; 79 | width: 11rem!important; 80 | height: 15rem!important; 81 | } 82 | 83 | .extra-network-cards { 84 | padding-bottom: 15rem!important; 85 | } 86 | 87 | 88 | .sixgod-slider-layout-container p{ 89 | 90 | display: flex; 91 | justify-content: center; 92 | padding: 10px; 93 | } 94 | .sixgod-slider-layout-container input{ 95 | width: 600px; 96 | } 97 | 98 | .sixgod-slider-layout-container label{ 99 | width: 80px; 100 | margin: 0 20px; 101 | } 102 | .sixgod-slider-layout-container button{ 103 | font-weight: bold; 104 | background-color:rgb(254, 215, 170); 105 | border-radius: 5px; 106 | margin: 5px 0; 107 | padding: 5px; 108 | text-align: center; 109 | cursor: pointer; 110 | color:rgb(234, 88, 12); 111 | padding: 10px; 112 | width: 200px; 113 | } 114 | 115 | --------------------------------------------------------------------------------