diff --git a/javascript/style.css b/javascript/style.css
index bcba1d2f4..f8a28d227 100644
--- a/javascript/style.css
+++ b/javascript/style.css
@@ -1,642 +1,642 @@
-:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;}
-a { font-weight: bold; cursor: pointer; }
-div.gradio-container{ max-width: unset !important; }
-div.form{ border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em; }
-div.compact{ gap: 1em; }
-div.gradio-html.min{ min-height: 0; }
-.block.gradio-checkbox { margin: 0.75em 1.5em 0 0; }
-.block.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker { border-width: 0 !important; box-shadow: none !important;}
-.block.gradio-gallery{ background: var(--input-background-fill); }
-.block.padded:not(.gradio-accordion) { padding: 0 !important; }
-.compact{ background: transparent !important; padding: 0 !important; }
-.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-900); }
-.gap.compact{ padding: 0; gap: 0.2em 0; }
-.gradio-container .prose a, .gradio-container .prose a:visited{ color: unset; text-decoration: none; }
-.gradio-dropdown .single-select{ white-space: nowrap; overflow: hidden; }
-.gradio-dropdown .token-remove.remove-all.remove-all{ display: none; }
-.gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); }
-.gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) { margin-bottom: 0; }
-.gradio-dropdown ul.options li.item { padding: 0.05em 0; }
-.gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-100); }
-.gradio-dropdown ul.options{ z-index: 3000; min-width: fit-content; max-width: inherit; white-space: nowrap; }
-.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; }
-.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ display: flex; }
-.gradio-dropdown.multiselect div.wrap-inner { overflow-x: hidden; overflow-y: auto; max-height: 50vh; overflow-wrap: anywhere; }
-.gradio-html div.wrap{ height: 100%; }
-.gradio-slider input[type="number"]{ width: 6em; }
-.hidden{ display: none; }
-
-/* general styled components */
-.gradio-button.tool{ max-width: 2.3em; min-width: 2.3em !important; height: 2.3em; align-self: end; line-height: 1em; border-radius: 0.5em; }
-.gradio-button.secondary-down{ background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); }
-.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; }
-.gradio-button.secondary-down:hover{ background: var(--button-secondary-background-fill-hover); color: var(--button-secondary-text-color-hover); }
-.checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; }
-.checkboxes-row > div{ flex: 0; white-space: nowrap; min-width: auto; }
-button.custom-button{
- border-radius: var(--button-large-radius);
- padding: var(--button-large-padding);
- font-weight: var(--button-large-text-weight);
- border: var(--button-border-width) solid var(--button-secondary-border-color);
- background: var(--button-secondary-background-fill);
- color: var(--button-secondary-text-color);
- font-size: var(--button-large-text-size);
- display: inline-flex;
- justify-content: center;
- align-items: center;
- transition: var(--button-transition);
- box-shadow: var(--button-shadow);
- text-align: center;
-}
-
-/* remove footer */
-footer { display: none !important; }
-
-/* themes */
-.theme-preview { display: none; position: fixed; border: 4px solid var(--neutral-600); box-shadow: 2px 2px 2px 2px var(--neutral-700); top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-width: 75vw; z-index: 999; }
-
-/* txt2img/img2img specific */
-.block.token-counter{
- position: absolute;
- display: inline-block;
- right: 1em;
- min-width: 0 !important;
- width: auto;
- z-index: 100;
- top: -0.75em;
-}
-
-.block.token-counter span{
- background: var(--input-background-fill) !important;
- box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075);
- border: 2px solid rgba(192,192,192,0.4) !important;
- border-radius: 0.4em;
-}
-
-.block.token-counter.error span{
- box-shadow: 0 0 0.0 0.3em rgba(255,0,0,0.15), inset 0 0 0.6em rgba(255,0,0,0.075);
- border: 2px solid rgba(255,0,0,0.4) !important;
-}
-
-.block.token-counter div{ display: inline; }
-.block.token-counter span{ padding: 0.1em 0.75em; }
-
-[id$=_subseed_show]{
- min-width: auto !important;
- flex-grow: 0 !important;
- display: flex;
-}
-
-[id$=_subseed_show] label{
- margin-bottom: 0.5em;
- align-self: end;
-}
-
-.performance { font-size: 0.85em; color: #444; }
-.performance p { display: inline-block; color: var(--body-text-color-subdued) !important }
-.performance .time { margin-right: 0; }
-
-@media screen and (min-width: 2500px) {
- #txt2img_gallery, #img2img_gallery { min-height: 768px; }
-}
-
-#txt2img_gallery img, #img2img_gallery img, #extras_gallery img { object-fit: scale-down; width: -webkit-fill-available !important; }
-
-#txt2img_generate_box, #img2img_generate_box { gap: 0.5em; flex-wrap: wrap-reverse; }
-#txt2img_actions_column, #img2img_actions_column { gap: 0.5em; }
-#txt2img_generate_box > button, #img2img_generate_box > button { height: 2.2em; line-height: 0; }
-#txt2img_generate_line2, #img2img_generate_line2 { display: flex; }
-#txt2img_generate_line2 > button, #img2img_generate_line2 > button, #extras_generate_box > button { height: 2.2em; line-height: 0; min-width: unset; display: block !important; }
-#txt2img_tools > div, #img2img_tools > div { justify-content: space-around; margin-top: 0.5em; margin-bottom: 0em; }
-#txt2img_tools > div > button, #img2img_tools > div > button { scale: 120%; }
-#refresh_txt2img_styles, #refresh_img2img_styles { height: 2.46em; margin-left: -8px; }
-
-.interrogate-col{
- min-width: 0 !important;
- max-width: fit-content;
- gap: 0.5em;
-}
-.interrogate-col > button{
- flex: 1;
-}
-
-#txtimg_hr_finalres{
- min-height: 0 !important;
- padding: .625rem .75rem;
- margin-left: -0.75em
-}
-
-#img2img_scale_resolution_preview.block{
- display: flex;
- align-items: end;
-}
-
-#txtimg_hr_finalres .resolution, #img2img_scale_resolution_preview .resolution{
- font-weight: bold;
-}
-
-div#extras_scale_to_tab div.form{
- flex-direction: row;
-}
-
-#img2img_column_batch{
- align-self: end;
- margin-bottom: 0.9em;
-}
-
-#img2img_unused_scale_by_slider{
- visibility: hidden;
- width: 0.5em;
- max-width: 0.5em;
- min-width: 0.5em;
-}
-
-.extra-network-cards{
- height: fit-content;
- max-height: 50vh;
- overflow-y: scroll;
- overflow-x: hidden;
- resize: vertical;
-}
-
-.inactive{
- opacity: 0.5;
-}
-
-[id$=_column_batch]{
- min-width: min(13.5em, 100%) !important;
-}
-
-div.dimensions-tools{
- min-width: 0 !important;
- max-width: fit-content;
- flex-direction: row;
- align-content: center;
-}
-
-div#extras_scale_to_tab div.form{
- flex-direction: row;
-}
-
-#mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{
- height: 480px !important;
- max-height: 480px !important;
- min-height: 480px !important;
-}
-
-#img2img_sketch, #img2maskimg, #inpaint_sketch {
- overflow: overlay !important;
- resize: auto;
- background: var(--panel-background-fill);
- z-index: 5;
-}
-
-.image-buttons button{
- min-width: auto;
-}
-
-.infotext {
- overflow-wrap: break-word;
-}
-
-/* settings */
-#quicksettings {
- width: fit-content;
- align-items: end;
-}
-
-#quicksettings > div, #quicksettings > fieldset{
- max-width: 24em;
- min-width: 24em;
- padding: 0;
- border: none;
- box-shadow: none;
- background: none;
-}
-
-#settings{
- display: flex;
- flex-flow: row wrap;
- gap: var(--layout-gap);
-}
-
-#settings div {
- border: none;
-}
-
-#settings > div.tab-content {
- flex: 100000 0 75%;
-}
-
-#settings > div.tab-content > div{
- border: none;
- padding: 0;
-}
-
-#settings > div.tab-nav{
- display: grid;
- grid-template-columns: repeat(auto-fill, .3em minmax(10em, 1fr));
- flex: 1 0 auto;
- width: 11em;
- align-self: flex-start;
- gap: var(--spacing-md);
-}
-
-#settings > div.tab-nav button{
- display: block;
- border: none;
- text-align: left;
- white-space: initial;
- padding: 0;
-}
-
-#settings > div.tab-nav > #settings_show_all_pages {
- padding: var(--size-2) var(--size-4);
-}
-
-#settings .block.gradio-checkbox {
- margin: 0;
- width: auto;
-}
-
-#settings .dirtyable {
- display: grid;
- grid-template-columns: .3em auto;
- gap: .5em;
-}
-
-#settings .dirtyable.hidden {
- display: none;
-}
-
-#settings .modification-indicator {
- width: 100%;
- height: 100%;
- border-radius: 1em !important;
- padding: 0;
-}
-
-#settings .modification-indicator:disabled {
- visibility: hidden;
-}
-
-#settings .modification-indicator.unsaved {
- background: var(--color-accent-soft);
-}
-
-#settings .modification-indicator.changed {
- background: var(--color-accent);
-}
-
-#settings .modification-indicator.changed.unsaved {
- background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%);
-}
-
-#settings_result{
- margin: 0 1.2em;
-}
-
-
-/* live preview */
-.progressDiv{
- position: relative;
- height: 20px;
- background: #b4c0cc;
- border-radius: 3px !important;
- margin-bottom: -3px;
-}
-
-.dark .progressDiv{
- background: #424c5b;
-}
-
-.progressDiv .progress{
- width: 0%;
- height: 20px;
- background: #0060df;
- color: white;
- font-weight: bold;
- line-height: 20px;
- padding: 0 8px 0 0;
- text-align: right;
- border-radius: 3px;
- overflow: visible;
- white-space: nowrap;
- padding: 0 0.5em;
-}
-
-.livePreview{
- position: absolute;
- z-index: 300;
- background-color: transparent;
- width: -webkit-fill-available;
-}
-
-.dark .livePreview{
- background-color: rgb(17 24 39 / var(--tw-bg-opacity));
-}
-
-.livePreview img{
- position: absolute;
- object-fit: contain;
- width: 100%;
- height: 100%;
-}
-
-/* fullscreen popup (ie in Lora's (i) button) */
-
-.popup-metadata{
- color: black;
- background: white;
- display: inline-block;
- padding: 1em;
- white-space: pre-wrap;
-}
-
-.global-popup{
- display: flex;
- position: fixed;
- z-index: 1001;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- overflow: auto;
- background-color: rgba(20, 20, 20, 0.95);
-}
-
-
-.global-popup-close:before {
- content: "×";
-}
-
-.global-popup-close{
- position: fixed;
- right: 0.25em;
- top: 0;
- cursor: pointer;
- color: white;
- font-size: 32pt;
-}
-
-.global-popup-inner{
- display: inline-block;
- margin: auto;
- padding: 2em;
-}
-
-/* fullpage image viewer */
-
-#lightboxModal{
- display: none;
- position: fixed;
- z-index: 1001;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- overflow: auto;
- background-color: rgba(20, 20, 20, 0.95);
- user-select: none;
- -webkit-user-select: none;
- flex-direction: column;
-}
-
-.modalControls {
- display: flex;
- gap: 1em;
- padding: 1em;
- background-color: rgba(0,0,0,0.2);
- position: absolute;
- width: fit-content;
- z-index: 1;
-}
-
-.modalClose {
- margin-left: auto;
-}
-
-.modalControls span{
- color: white;
- font-size: 35px;
- font-weight: bold;
- cursor: pointer;
- width: 1em;
-}
-
-.modalControls span:hover, .modalControls span:focus{
- color: #999;
- text-decoration: none;
-}
-
-#lightboxModal > img {
- display: block;
- margin: auto;
- width: auto;
-}
-
-#lightboxModal > img.modalImageFullscreen{
- object-fit: contain;
- height: 100%;
- width: 100%;
- min-height: 0;
-}
-
-table.settings-value-table{
- background: white;
- border-collapse: collapse;
- margin: 1em;
- border: 4px solid white;
-}
-
-table.settings-value-table td{
- padding: 0.4em;
- border: 1px solid #ccc;
- max-width: 36em;
-}
-
-.modalPrev, .modalNext {
- cursor: pointer;
- position: absolute;
- top: 0;
- width: auto;
- height: 100vh;
- line-height: 100vh;
- text-align: center;
- padding: 16px;
- margin-top: -50px;
- color: white;
- font-weight: bold;
- font-size: 20px;
- transition: 0.6s ease;
- border-radius: 0 3px 3px 0;
- user-select: none;
- -webkit-user-select: none;
-}
-
-.modalNext {
- right: 0;
- border-radius: 3px 0 0 3px;
-}
-
-.modalPrev:hover, .modalNext:hover {
- background-color: rgba(0, 0, 0, 0.8);
-}
-
-#imageARPreview {
- position: absolute;
- top: 0px;
- left: 0px;
- border: 2px solid red;
- background: rgba(255, 0, 0, 0.3);
- z-index: 900;
- pointer-events: none;
- display: none;
-}
-
-/* context menu (ie for the generate button) */
-
-#context-menu{
- z-index:9999;
- position:absolute;
- display:block;
- padding:0px 0;
- border:2px solid #a55000;
- border-radius:8px;
- box-shadow:1px 1px 2px #CE6400;
- width: 200px;
-}
-
-.context-menu-items{
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-.context-menu-items a{
- display:block;
- padding:5px;
- cursor:pointer;
-}
-
-.context-menu-items a:hover{
- background: #a55000;
-}
-
-
-/* extensions */
-
-#tab_extensions table{ border-collapse: collapse; }
-#tab_extensions table td, #tab_extensions table th { border: 1px solid #ccc; padding: 0.25em 0.5em; }
-#tab_extensions table input[type="checkbox"] { margin-right: 0.5em; appearance: checkbox; }
-#tab_extensions button{ max-width: 16em; }
-#tab_extensions input[disabled="disabled"]{ opacity: 0.5; }
-.extension-tag{ font-weight: bold; font-size: 95%; }
-#extensions .name{ font-size: 1.1rem }
-#extensions .type{ opacity: 0.5; font-size: 90%; text-align: center; }
-#extensions .version{ opacity: 0.7; }
-#extensions .info{ margin: 0; }
-#extensions .date{ opacity: 0.85; font-size: 90%; }
-.extension-button { font-size: 95% !important; width: 6em; }
-
-/* extra networks */
-.extra-networks > div > [id *= '_extra_']{ margin: 0.3em; }
-.extra-network-subdirs{ padding: 0.2em 0.35em; }
-.extra-network-subdirs button{ margin: 0 0.15em; }
-.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; }
-#txt2img_extra_view, #img2img_extra_view { width: auto; }
-.extra-network-cards .nocards, .extra-network-thumbs .nocards{ margin: 1.25em 0.5em 0.5em 0.5em; }
-.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ font-size: 1.5em; margin-bottom: 1em; }
-.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ margin-left: 0.5em; }
-.extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{
- display: none;
- position: absolute;
- right: 0;
- color: white;
- text-shadow: 2px 2px 3px black;
- padding: 0.25em;
- font-size: 22pt;
- width: 1.5em;
-}
-.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; }
-.extra-network-thumbs { display: flex; flex-flow: row wrap; gap: 10px; }
-.extra-network-cards .card .additional a:hover, .extra-network-thumbs .card .additional a:hover { color: darkorange }
-.extra-network-thumbs .card {
- display: inline-block;
- height: 9em;
- width: 9em;
- cursor: pointer;
- background-image: url('../html/card-no-preview.png');
- background-size: cover;
- background-position: center center;
- position: relative;
-}
-.extra-network-cards .card .additional, .extra-network-thumbs .card .additional { white-space: nowrap; overflow: hidden; }
-.extra-network-thumbs .card:hover .additional a { display: inline-block; }
-.extra-network-thumbs .actions .name {
- position: absolute;
- bottom: 0;
- font-size: 10px;
- padding: 3px;
- width: 100%;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- background: rgba(0,0,0,.5);
- color: white;
-}
-.extra-network-thumbs .card:hover .actions .name { white-space: normal; word-break: break-all; }
-.extra-network-cards .card{
- display: inline-block;
- margin: 0.5em;
- width: 16em;
- height: 24em;
- box-shadow: 0 0 5px rgba(128, 128, 128, 0.5);
- border-radius: 0.2em;
- position: relative;
- background-size: cover;
- background-position: center;
- overflow: hidden;
- cursor: pointer;
- background-image: url('../html/card-no-preview.png')
-}
-.extra-network-cards .card:hover { box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); }
-.extra-network-cards .card .actions .additional, .extra-network-thumbs .card .actions .additional{ display: none; }
-.extra-network-cards .card .actions{
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 0.5em;
- background: rgba(0,0,0,0.5);
- box-shadow: 0 0 0.25em 0.25em rgba(0,0,0,0.5);
- text-shadow: 0 0 0.2em black;
-}
-.extra-network-cards .card .actions *{ color: white; }
-.extra-network-cards .card .actions:hover { box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; }
-.extra-network-cards .card .actions .name { font-size: 1.7em; font-weight: bold; line-break: anywhere; }
-.extra-network-cards .card .actions .description { display: block; max-height: 3em; white-space: pre-wrap; line-height: 1.1; }
-.extra-network-cards .card .actions .description:hover { max-height: none; }
-.extra-network-cards .card .actions:hover .additional, .extra-network-thumbs .card:hover .additional{ display: block; }
-.extra-network-cards .card ul{ margin: 0.25em 0 0.75em 0.25em; cursor: unset; }
-.extra-network-cards .card ul a{ cursor: pointer; }
-.extra-network-cards .card ul a:hover{ color: red; }
-
-/* controlnet */
-.controlnet_control_type .controlnet_control_type_filter_group .wrap:last-of-type { display: grid; grid-auto-flow: row; grid-template-columns: repeat(4, minmax(0, 1fr)); }
-fieldset.controlnet_resize_mode_radio .wrap:last-of-type, fieldset.controlnet_control_mode_radio .wrap:last-of-type { flex-direction: column; }
-div.controlnet_preprocessor_model { display: grid; grid-auto-flow: row; grid-template-columns: 1fr max-content; }
-div.controlnet_preprocessor_model button.gradio-button { align-self: center; }
-div.controlnet_weight_steps > div.form { display: grid; grid-template: repeat(2, 1fr) / repeat(2, 1fr); }
-div.controlnet_weight_steps .controlnet_control_weight_slider { grid-column: 1 / -1; }
-div.controlnet_image_controls { display: grid; grid-template-columns: repeat(4, 1fr); }
-div.controlnet_image_controls .controlnet_invert_warning { grid-column: 1 / -1; }
-div.controlnet_image_controls button { justify-self: center; }
-div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: row; }
-
-/* specific elements */
-#modelmerger_interp_description { margin-top: 1em; margin-bottom: 1em; }
-#scripts_alwayson_txt2img, #scripts_alwayson_img2img { display: grid }
-#extras_generate, #extras_interrupt, #extras_skip { display: block !important; position: relative; height: 36px; }
-#extras_upscale { margin-top: 10px }
-#refresh_tac_refreshTempFiles { display: none; }
+:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;}
+a { font-weight: bold; cursor: pointer; }
+div.gradio-container{ max-width: unset !important; }
+div.form{ border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em; }
+div.compact{ gap: 1em; }
+div.gradio-html.min{ min-height: 0; }
+.block.gradio-checkbox { margin: 0.75em 1.5em 0 0; }
+.block.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker { border-width: 0 !important; box-shadow: none !important;}
+.block.gradio-gallery{ background: var(--input-background-fill); }
+.block.padded:not(.gradio-accordion) { padding: 0 !important; }
+.compact{ background: transparent !important; padding: 0 !important; }
+.dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-900); }
+.gap.compact{ padding: 0; gap: 0.2em 0; }
+.gradio-container .prose a, .gradio-container .prose a:visited{ color: unset; text-decoration: none; }
+.gradio-dropdown .single-select{ white-space: nowrap; overflow: hidden; }
+.gradio-dropdown .token-remove.remove-all.remove-all{ display: none; }
+.gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); }
+.gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) { margin-bottom: 0; }
+.gradio-dropdown ul.options li.item { padding: 0.05em 0; }
+.gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-100); }
+.gradio-dropdown ul.options{ z-index: 3000; min-width: fit-content; max-width: inherit; white-space: nowrap; }
+.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; }
+.gradio-dropdown.multiselect .token-remove.remove-all.remove-all{ display: flex; }
+.gradio-dropdown.multiselect div.wrap-inner { overflow-x: hidden; overflow-y: auto; max-height: 50vh; overflow-wrap: anywhere; }
+.gradio-html div.wrap{ height: 100%; }
+.gradio-slider input[type="number"]{ width: 6em; }
+.hidden{ display: none; }
+
+/* general styled components */
+.gradio-button.tool{ max-width: 2.3em; min-width: 2.3em !important; height: 2.3em; align-self: end; line-height: 1em; border-radius: 0.5em; }
+.gradio-button.secondary-down{ background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); }
+.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; }
+.gradio-button.secondary-down:hover{ background: var(--button-secondary-background-fill-hover); color: var(--button-secondary-text-color-hover); }
+.checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; }
+.checkboxes-row > div{ flex: 0; white-space: nowrap; min-width: auto; }
+button.custom-button{
+ border-radius: var(--button-large-radius);
+ padding: var(--button-large-padding);
+ font-weight: var(--button-large-text-weight);
+ border: var(--button-border-width) solid var(--button-secondary-border-color);
+ background: var(--button-secondary-background-fill);
+ color: var(--button-secondary-text-color);
+ font-size: var(--button-large-text-size);
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ transition: var(--button-transition);
+ box-shadow: var(--button-shadow);
+ text-align: center;
+}
+
+/* remove footer */
+footer { display: none !important; }
+
+/* themes */
+.theme-preview { display: none; position: fixed; border: 4px solid var(--neutral-600); box-shadow: 2px 2px 2px 2px var(--neutral-700); top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-width: 75vw; z-index: 999; }
+
+/* txt2img/img2img specific */
+.block.token-counter{
+ position: absolute;
+ display: inline-block;
+ right: 1em;
+ min-width: 0 !important;
+ width: auto;
+ z-index: 100;
+ top: -0.75em;
+}
+
+.block.token-counter span{
+ background: var(--input-background-fill) !important;
+ box-shadow: 0 0 0.0 0.3em rgba(192,192,192,0.15), inset 0 0 0.6em rgba(192,192,192,0.075);
+ border: 2px solid rgba(192,192,192,0.4) !important;
+ border-radius: 0.4em;
+}
+
+.block.token-counter.error span{
+ box-shadow: 0 0 0.0 0.3em rgba(255,0,0,0.15), inset 0 0 0.6em rgba(255,0,0,0.075);
+ border: 2px solid rgba(255,0,0,0.4) !important;
+}
+
+.block.token-counter div{ display: inline; }
+.block.token-counter span{ padding: 0.1em 0.75em; }
+
+[id$=_subseed_show]{
+ min-width: auto !important;
+ flex-grow: 0 !important;
+ display: flex;
+}
+
+[id$=_subseed_show] label{
+ margin-bottom: 0.5em;
+ align-self: end;
+}
+
+.performance { font-size: 0.85em; color: #444; }
+.performance p { display: inline-block; color: var(--body-text-color-subdued) !important }
+.performance .time { margin-right: 0; }
+
+@media screen and (min-width: 2500px) {
+ #txt2img_gallery, #img2img_gallery { min-height: 768px; }
+}
+
+#txt2img_gallery img, #img2img_gallery img, #extras_gallery img { object-fit: scale-down; width: -webkit-fill-available !important; }
+
+#txt2img_generate_box, #img2img_generate_box { gap: 0.5em; flex-wrap: wrap-reverse; }
+#txt2img_actions_column, #img2img_actions_column { gap: 0.5em; }
+#txt2img_generate_box > button, #img2img_generate_box > button { height: 2.2em; line-height: 0; }
+#txt2img_generate_line2, #img2img_generate_line2 { display: flex; }
+#txt2img_generate_line2 > button, #img2img_generate_line2 > button, #extras_generate_box > button { height: 2.2em; line-height: 0; min-width: unset; display: block !important; }
+#txt2img_tools > div, #img2img_tools > div { justify-content: space-around; margin-top: 0.5em; margin-bottom: 0em; }
+#txt2img_tools > div > button, #img2img_tools > div > button { scale: 120%; }
+#refresh_txt2img_styles, #refresh_img2img_styles { height: 2.46em; margin-left: -8px; }
+
+.interrogate-col{
+ min-width: 0 !important;
+ max-width: fit-content;
+ gap: 0.5em;
+}
+.interrogate-col > button{
+ flex: 1;
+}
+
+#txtimg_hr_finalres{
+ min-height: 0 !important;
+ padding: .625rem .75rem;
+ margin-left: -0.75em
+}
+
+#img2img_scale_resolution_preview.block{
+ display: flex;
+ align-items: end;
+}
+
+#txtimg_hr_finalres .resolution, #img2img_scale_resolution_preview .resolution{
+ font-weight: bold;
+}
+
+div#extras_scale_to_tab div.form{
+ flex-direction: row;
+}
+
+#img2img_column_batch{
+ align-self: end;
+ margin-bottom: 0.9em;
+}
+
+#img2img_unused_scale_by_slider{
+ visibility: hidden;
+ width: 0.5em;
+ max-width: 0.5em;
+ min-width: 0.5em;
+}
+
+.extra-network-cards{
+ height: fit-content;
+ max-height: 50vh;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ resize: vertical;
+}
+
+.inactive{
+ opacity: 0.5;
+}
+
+[id$=_column_batch]{
+ min-width: min(13.5em, 100%) !important;
+}
+
+div.dimensions-tools{
+ min-width: 0 !important;
+ max-width: fit-content;
+ flex-direction: row;
+ align-content: center;
+}
+
+div#extras_scale_to_tab div.form{
+ flex-direction: row;
+}
+
+#mode_img2img .gradio-image > div.fixed-height, #mode_img2img .gradio-image > div.fixed-height img{
+ height: 480px !important;
+ max-height: 480px !important;
+ min-height: 480px !important;
+}
+
+#img2img_sketch, #img2maskimg, #inpaint_sketch {
+ overflow: overlay !important;
+ resize: auto;
+ background: var(--panel-background-fill);
+ z-index: 5;
+}
+
+.image-buttons button{
+ min-width: auto;
+}
+
+.infotext {
+ overflow-wrap: break-word;
+}
+
+/* settings */
+#quicksettings {
+ width: fit-content;
+ align-items: end;
+}
+
+#quicksettings > div, #quicksettings > fieldset{
+ max-width: 24em;
+ min-width: 24em;
+ padding: 0;
+ border: none;
+ box-shadow: none;
+ background: none;
+}
+
+#settings{
+ display: flex;
+ flex-flow: row wrap;
+ gap: var(--layout-gap);
+}
+
+#settings div {
+ border: none;
+}
+
+#settings > div.tab-content {
+ flex: 100000 0 75%;
+}
+
+#settings > div.tab-content > div{
+ border: none;
+ padding: 0;
+}
+
+#settings > div.tab-nav{
+ display: grid;
+ grid-template-columns: repeat(auto-fill, .3em minmax(10em, 1fr));
+ flex: 1 0 auto;
+ width: 11em;
+ align-self: flex-start;
+ gap: var(--spacing-md);
+}
+
+#settings > div.tab-nav button{
+ display: block;
+ border: none;
+ text-align: left;
+ white-space: initial;
+ padding: 0;
+}
+
+#settings > div.tab-nav > #settings_show_all_pages {
+ padding: var(--size-2) var(--size-4);
+}
+
+#settings .block.gradio-checkbox {
+ margin: 0;
+ width: auto;
+}
+
+#settings .dirtyable {
+ display: grid;
+ grid-template-columns: .3em auto;
+ gap: .5em;
+}
+
+#settings .dirtyable.hidden {
+ display: none;
+}
+
+#settings .modification-indicator {
+ width: 100%;
+ height: 100%;
+ border-radius: 1em !important;
+ padding: 0;
+}
+
+#settings .modification-indicator:disabled {
+ visibility: hidden;
+}
+
+#settings .modification-indicator.unsaved {
+ background: var(--color-accent-soft);
+}
+
+#settings .modification-indicator.changed {
+ background: var(--color-accent);
+}
+
+#settings .modification-indicator.changed.unsaved {
+ background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%);
+}
+
+#settings_result{
+ margin: 0 1.2em;
+}
+
+
+/* live preview */
+.progressDiv{
+ position: relative;
+ height: 20px;
+ background: #b4c0cc;
+ border-radius: 3px !important;
+ margin-bottom: -3px;
+}
+
+.dark .progressDiv{
+ background: #424c5b;
+}
+
+.progressDiv .progress{
+ width: 0%;
+ height: 20px;
+ background: #0060df;
+ color: white;
+ font-weight: bold;
+ line-height: 20px;
+ padding: 0 8px 0 0;
+ text-align: right;
+ border-radius: 3px;
+ overflow: visible;
+ white-space: nowrap;
+ padding: 0 0.5em;
+}
+
+.livePreview{
+ position: absolute;
+ z-index: 300;
+ background-color: transparent;
+ width: -webkit-fill-available;
+}
+
+.dark .livePreview{
+ background-color: rgb(17 24 39 / var(--tw-bg-opacity));
+}
+
+.livePreview img{
+ position: absolute;
+ object-fit: contain;
+ width: 100%;
+ height: 100%;
+}
+
+/* fullscreen popup (ie in Lora's (i) button) */
+
+.popup-metadata{
+ color: black;
+ background: white;
+ display: inline-block;
+ padding: 1em;
+ white-space: pre-wrap;
+}
+
+.global-popup{
+ display: flex;
+ position: fixed;
+ z-index: 1001;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(20, 20, 20, 0.95);
+}
+
+
+.global-popup-close:before {
+ content: "×";
+}
+
+.global-popup-close{
+ position: fixed;
+ right: 0.25em;
+ top: 0;
+ cursor: pointer;
+ color: white;
+ font-size: 32pt;
+}
+
+.global-popup-inner{
+ display: inline-block;
+ margin: auto;
+ padding: 2em;
+}
+
+/* fullpage image viewer */
+
+#lightboxModal{
+ display: none;
+ position: fixed;
+ z-index: 1001;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(20, 20, 20, 0.95);
+ user-select: none;
+ -webkit-user-select: none;
+ flex-direction: column;
+}
+
+.modalControls {
+ display: flex;
+ gap: 1em;
+ padding: 1em;
+ background-color: rgba(0,0,0,0.2);
+ position: absolute;
+ width: fit-content;
+ z-index: 1;
+}
+
+.modalClose {
+ margin-left: auto;
+}
+
+.modalControls span{
+ color: white;
+ font-size: 35px;
+ font-weight: bold;
+ cursor: pointer;
+ width: 1em;
+}
+
+.modalControls span:hover, .modalControls span:focus{
+ color: #999;
+ text-decoration: none;
+}
+
+#lightboxModal > img {
+ display: block;
+ margin: auto;
+ width: auto;
+}
+
+#lightboxModal > img.modalImageFullscreen{
+ object-fit: contain;
+ height: 100%;
+ width: 100%;
+ min-height: 0;
+}
+
+table.settings-value-table{
+ background: white;
+ border-collapse: collapse;
+ margin: 1em;
+ border: 4px solid white;
+}
+
+table.settings-value-table td{
+ padding: 0.4em;
+ border: 1px solid #ccc;
+ max-width: 36em;
+}
+
+.modalPrev, .modalNext {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: auto;
+ height: 100vh;
+ line-height: 100vh;
+ text-align: center;
+ padding: 16px;
+ margin-top: -50px;
+ color: white;
+ font-weight: bold;
+ font-size: 20px;
+ transition: 0.6s ease;
+ border-radius: 0 3px 3px 0;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+.modalNext {
+ right: 0;
+ border-radius: 3px 0 0 3px;
+}
+
+.modalPrev:hover, .modalNext:hover {
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+#imageARPreview {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ border: 2px solid red;
+ background: rgba(255, 0, 0, 0.3);
+ z-index: 900;
+ pointer-events: none;
+ display: none;
+}
+
+/* context menu (ie for the generate button) */
+
+#context-menu{
+ z-index:9999;
+ position:absolute;
+ display:block;
+ padding:0px 0;
+ border:2px solid #a55000;
+ border-radius:8px;
+ box-shadow:1px 1px 2px #CE6400;
+ width: 200px;
+}
+
+.context-menu-items{
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.context-menu-items a{
+ display:block;
+ padding:5px;
+ cursor:pointer;
+}
+
+.context-menu-items a:hover{
+ background: #a55000;
+}
+
+
+/* extensions */
+
+#tab_extensions table{ border-collapse: collapse; }
+#tab_extensions table td, #tab_extensions table th { border: 1px solid #ccc; padding: 0.25em 0.5em; }
+#tab_extensions table input[type="checkbox"] { margin-right: 0.5em; appearance: checkbox; }
+#tab_extensions button{ max-width: 16em; }
+#tab_extensions input[disabled="disabled"]{ opacity: 0.5; }
+.extension-tag{ font-weight: bold; font-size: 95%; }
+#extensions .name{ font-size: 1.1rem }
+#extensions .type{ opacity: 0.5; font-size: 90%; text-align: center; }
+#extensions .version{ opacity: 0.7; }
+#extensions .info{ margin: 0; }
+#extensions .date{ opacity: 0.85; font-size: 90%; }
+.extension-button { font-size: 95% !important; width: 6em; }
+
+/* extra networks */
+.extra-networks > div > [id *= '_extra_']{ margin: 0.3em; }
+.extra-network-subdirs{ padding: 0.2em 0.35em; }
+.extra-network-subdirs button{ margin: 0 0.15em; }
+.extra-networks .tab-nav .search{ display: inline-block; max-width: 16em; margin: 0.3em; align-self: center; width: 16em; }
+#txt2img_extra_view, #img2img_extra_view { width: auto; }
+.extra-network-cards .nocards, .extra-network-thumbs .nocards{ margin: 1.25em 0.5em 0.5em 0.5em; }
+.extra-network-cards .nocards h1, .extra-network-thumbs .nocards h1{ font-size: 1.5em; margin-bottom: 1em; }
+.extra-network-cards .nocards li, .extra-network-thumbs .nocards li{ margin-left: 0.5em; }
+.extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{
+ display: none;
+ position: absolute;
+ right: 0;
+ color: white;
+ text-shadow: 2px 2px 3px black;
+ padding: 0.25em;
+ font-size: 22pt;
+ width: 1.5em;
+}
+.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; }
+.extra-network-thumbs { display: flex; flex-flow: row wrap; gap: 10px; }
+.extra-network-cards .card .additional a:hover, .extra-network-thumbs .card .additional a:hover { color: darkorange }
+.extra-network-thumbs .card {
+ display: inline-block;
+ height: 9em;
+ width: 9em;
+ cursor: pointer;
+ background-image: url('../html/card-no-preview.png');
+ background-size: cover;
+ background-position: center center;
+ position: relative;
+}
+.extra-network-cards .card .additional, .extra-network-thumbs .card .additional { white-space: nowrap; overflow: hidden; }
+.extra-network-thumbs .card:hover .additional a { display: inline-block; }
+.extra-network-thumbs .actions .name {
+ position: absolute;
+ bottom: 0;
+ font-size: 10px;
+ padding: 3px;
+ width: 100%;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ background: rgba(0,0,0,.5);
+ color: white;
+}
+.extra-network-thumbs .card:hover .actions .name { white-space: normal; word-break: break-all; }
+.extra-network-cards .card{
+ display: inline-block;
+ margin: 0.5em;
+ width: 16em;
+ height: 24em;
+ box-shadow: 0 0 5px rgba(128, 128, 128, 0.5);
+ border-radius: 0.2em;
+ position: relative;
+ background-size: cover;
+ background-position: center;
+ overflow: hidden;
+ cursor: pointer;
+ background-image: url('../html/card-no-preview.png')
+}
+.extra-network-cards .card:hover { box-shadow: 0 0 2px 0.3em rgba(0, 128, 255, 0.35); }
+.extra-network-cards .card .actions .additional, .extra-network-thumbs .card .actions .additional{ display: none; }
+.extra-network-cards .card .actions{
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 0.5em;
+ background: rgba(0,0,0,0.5);
+ box-shadow: 0 0 0.25em 0.25em rgba(0,0,0,0.5);
+ text-shadow: 0 0 0.2em black;
+}
+.extra-network-cards .card .actions *{ color: white; }
+.extra-network-cards .card .actions:hover { box-shadow: 0 0 0.75em 0.75em rgba(0,0,0,0.5) !important; }
+.extra-network-cards .card .actions .name { font-size: 1.7em; font-weight: bold; line-break: anywhere; }
+.extra-network-cards .card .actions .description { display: block; max-height: 3em; white-space: pre-wrap; line-height: 1.1; }
+.extra-network-cards .card .actions .description:hover { max-height: none; }
+.extra-network-cards .card .actions:hover .additional, .extra-network-thumbs .card:hover .additional{ display: block; }
+.extra-network-cards .card ul{ margin: 0.25em 0 0.75em 0.25em; cursor: unset; }
+.extra-network-cards .card ul a{ cursor: pointer; }
+.extra-network-cards .card ul a:hover{ color: red; }
+
+/* controlnet */
+.controlnet_control_type .controlnet_control_type_filter_group .wrap:last-of-type { display: grid; grid-auto-flow: row; grid-template-columns: repeat(4, minmax(0, 1fr)); }
+fieldset.controlnet_resize_mode_radio .wrap:last-of-type, fieldset.controlnet_control_mode_radio .wrap:last-of-type { flex-direction: column; }
+div.controlnet_preprocessor_model { display: grid; grid-auto-flow: row; grid-template-columns: 1fr max-content; }
+div.controlnet_preprocessor_model button.gradio-button { align-self: center; }
+div.controlnet_weight_steps > div.form { display: grid; grid-template: repeat(2, 1fr) / repeat(2, 1fr); }
+div.controlnet_weight_steps .controlnet_control_weight_slider { grid-column: 1 / -1; }
+div.controlnet_image_controls { display: grid; grid-template-columns: repeat(4, 1fr); }
+div.controlnet_image_controls .controlnet_invert_warning { grid-column: 1 / -1; }
+div.controlnet_image_controls button { justify-self: center; }
+div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: row; }
+
+/* specific elements */
+#modelmerger_interp_description { margin-top: 1em; margin-bottom: 1em; }
+#scripts_alwayson_txt2img, #scripts_alwayson_img2img { display: grid }
+#extras_generate, #extras_interrupt, #extras_skip { display: block !important; position: relative; height: 36px; }
+#extras_upscale { margin-top: 10px }
+#refresh_tac_refreshTempFiles { display: none; }
diff --git a/modules/shared.py b/modules/shared.py
index 3548fc439..04fc3c994 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -1,883 +1,883 @@
-import os
-import sys
-import time
-import json
-import datetime
-import urllib.request
-from enum import Enum
-import gradio as gr
-import tqdm
-import requests
-from modules import errors, ui_components, shared_items, cmd_args
-from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir # pylint: disable=W0611
-import modules.interrogate
-import modules.memmon
-import modules.styles
-import modules.devices as devices
-import modules.paths_internal as paths
-from installer import log as central_logger # pylint: disable=E0611
-
-
-errors.install(gr)
-demo: gr.Blocks = None
-log = central_logger
-progress_print_out = sys.stdout
-parser = cmd_args.parser
-url = 'https://github.com/vladmandic/automatic'
-cmd_opts, _ = parser.parse_known_args()
-hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
-xformers_available = False
-clip_model = None
-interrogator = modules.interrogate.InterrogateModels("interrogate")
-sd_upscalers = []
-face_restorers = []
-tab_names = []
-options_templates = {}
-hypernetworks = {}
-loaded_hypernetworks = []
-gradio_theme = gr.themes.Base()
-settings_components = None
-latent_upscale_default_mode = "Latent"
-latent_upscale_modes = {
- "Latent": {"mode": "bilinear", "antialias": False},
- "Latent (antialiased)": {"mode": "bilinear", "antialias": True},
- "Latent (bicubic)": {"mode": "bicubic", "antialias": False},
- "Latent (bicubic antialiased)": {"mode": "bicubic", "antialias": True},
- "Latent (nearest)": {"mode": "nearest", "antialias": False},
- "Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False},
-}
-restricted_opts = {
- "samples_filename_pattern",
- "directories_filename_pattern",
- "outdir_samples",
- "outdir_txt2img_samples",
- "outdir_img2img_samples",
- "outdir_extras_samples",
- "outdir_grids",
- "outdir_txt2img_grids",
- "outdir_save",
- "outdir_init_images"
-}
-ui_reorder_categories = [
- "inpaint",
- "sampler",
- "checkboxes",
- "hires_fix",
- "dimensions",
- "cfg",
- "seed",
- "batch",
- "override_settings",
- "scripts",
-]
-
-
-class Backend(Enum):
- ORIGINAL = 1
- DIFFUSERS = 2
-
-
-def reload_hypernetworks():
- from modules.hypernetworks import hypernetwork
- global hypernetworks # pylint: disable=W0603
- hypernetworks = hypernetwork.list_hypernetworks(opts.hypernetwork_dir)
-
-
-class State:
- skipped = False
- interrupted = False
- paused = False
- job = ""
- job_no = 0
- job_count = 0
- processing_has_refined_job_count = False
- job_timestamp = '0'
- sampling_step = 0
- sampling_steps = 0
- current_latent = None
- current_image = None
- current_image_sampling_step = 0
- id_live_preview = 0
- textinfo = None
- time_start = None
- need_restart = False
- server_start = None
- oom = False
-
- def skip(self):
- log.debug('Requested skip')
- self.skipped = True
-
- def interrupt(self):
- log.debug('Requested interrupt')
- self.interrupted = True
-
- def pause(self):
- self.paused = not self.paused
- log.debug(f'Requested {"pause" if self.paused else "continue"}')
-
- def nextjob(self):
- if opts.live_previews_enable and opts.show_progress_every_n_steps == -1:
- self.do_set_current_image()
- self.job_no += 1
- self.sampling_step = 0
- self.current_image_sampling_step = 0
-
- def dict(self):
- obj = {
- "skipped": self.skipped,
- "interrupted": self.interrupted,
- "job": self.job,
- "job_count": self.job_count,
- "job_timestamp": self.job_timestamp,
- "job_no": self.job_no,
- "sampling_step": self.sampling_step,
- "sampling_steps": self.sampling_steps,
- }
- return obj
-
- def begin(self):
- self.sampling_step = 0
- self.job_count = -1
- self.processing_has_refined_job_count = False
- self.job_no = 0
- self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
- self.current_latent = None
- self.current_image = None
- self.current_image_sampling_step = 0
- self.id_live_preview = 0
- self.skipped = False
- self.interrupted = False
- self.paused = False
- self.textinfo = None
- self.time_start = time.time()
- devices.torch_gc()
-
- def end(self):
- self.job = ""
- self.job_count = 0
- self.paused = False
- devices.torch_gc()
-
- def set_current_image(self):
- """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this"""
- if not parallel_processing_allowed:
- return
- if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.live_previews_enable and opts.show_progress_every_n_steps != -1:
- self.do_set_current_image()
-
- def do_set_current_image(self):
- if self.current_latent is None:
- return
- import modules.sd_samplers # pylint: disable=W0621
- if opts.show_progress_grid:
- self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent))
- else:
- self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent))
- self.current_image_sampling_step = self.sampling_step
-
- def assign_current_image(self, image):
- self.current_image = image
- self.id_live_preview += 1
-
-state = State()
-state.server_start = time.time()
-
-
-class OptionInfo:
- def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, comment_before='', comment_after=''):
- self.default = default
- self.label = label
- self.component = component
- self.component_args = component_args
- self.onchange = onchange
- self.section = section
- self.refresh = refresh
- self.comment_before = comment_before # HTML text that will be added after label in UI
- self.comment_after = comment_after # HTML text that will be added before label in UI
-
- def link(self, label, uri):
- self.comment_before += f"[{label}]"
- return self
-
- def js(self, label, js_func):
- self.comment_before += f"[{label}]"
- return self
-
- def info(self, info):
- self.comment_after += f"({info})"
- return self
-
- def needs_restart(self):
- self.comment_after += " (requires restart)"
- return self
-
-
-def options_section(section_identifier, options_dict):
- for v in options_dict.values():
- v.section = section_identifier
- return options_dict
-
-
-def list_checkpoint_tiles():
- import modules.sd_models # pylint: disable=W0621
- return modules.sd_models.checkpoint_tiles()
-
-
-default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt"
-
-
-def refresh_checkpoints():
- import modules.sd_models # pylint: disable=W0621
- return modules.sd_models.list_models()
-
-
-def list_samplers():
- import modules.sd_samplers # pylint: disable=W0621
- modules.sd_samplers.set_samplers()
- return modules.sd_samplers.all_samplers
-
-def list_themes():
- fn = os.path.join('html', 'themes.json')
- if not os.path.exists(fn):
- refresh_themes()
- if os.path.exists(fn):
- with open(fn, mode='r', encoding='utf=8') as f:
- res = json.loads(f.read())
- else:
- res = []
- builtin = ["black-orange", "gradio/default", "gradio/base", "gradio/glass", "gradio/monochrome", "gradio/soft"]
- themes = sorted(set(builtin + [x['id'] for x in res if x['status'] == 'RUNNING' and 'test' not in x['id'].lower()]), key=str.casefold)
- return themes
-
-
-def lora_disable():
- if opts.lora_disable:
- if 'Lora' not in opts.disabled_extensions:
- opts.data['disabled_extensions'].append('Lora')
- else:
- opts.data['disabled_extensions'] = [x for x in opts.disabled_extensions if x != 'Lora']
-
-
-def refresh_themes():
- try:
- req = requests.get('https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/subdomains.json', timeout=5)
- if req.status_code == 200:
- res = req.json()
- fn = os.path.join('html', 'themes.json')
- with open(fn, mode='w', encoding='utf=8') as f:
- f.write(json.dumps(res))
- else:
- log.error('Error refreshing UI themes')
- except:
- log.error('Exception refreshing UI themes')
-
-
-if devices.backend == "cpu":
- cross_attention_optimization_default = "Doggettx's"
-elif devices.backend == "mps":
- cross_attention_optimization_default = "Doggettx's"
-elif devices.backend == "ipex":
- cross_attention_optimization_default = "InvokeAI's"
-elif devices.backend == "directml":
- cross_attention_optimization_default = "Sub-quadratic"
-elif devices.backend == "rocm":
- cross_attention_optimization_default = "Sub-quadratic"
-else: # cuda
- cross_attention_optimization_default ="Scaled-Dot-Product"
-
-options_templates.update(options_section(('sd', "Stable Diffusion"), {
- "sd_model_checkpoint": OptionInfo(default_checkpoint, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
- "sd_checkpoint_cache": OptionInfo(0, "Number of cached model checkpoints", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
- "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAE checkpoints", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
- "sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
- "sd_model_dict": OptionInfo('None', "Stable Diffusion checkpoint dict", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
- "sd_vae_sliced_encode": OptionInfo(False, "Enable splitting of hires batch processing"),
- "stream_load": OptionInfo(False, "When loading models attempt stream loading optimized for slow or network storage"),
- "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"),
- "cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
- "cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
- "sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
- "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
- "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
- "prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }),
- "prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"),
- "always_batch_cond_uncond": OptionInfo(False, "Disable conditional batching enabled on low memory systems"),
- "enable_quantization": OptionInfo(True, "Enable samplers quantization for sharper and cleaner results"),
- "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
- "sd_backend": OptionInfo("Original", "Stable Diffusion backend (experimental)", gr.Radio, lambda: {"choices": ["Original", "Diffusers"] }),
-}))
-
-options_templates.update(options_section(('cuda', "Compute Settings"), {
- "memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
- "precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}),
- "cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}),
- "no_half": OptionInfo(False, "Use full precision for model (--no-half)", None, None, None),
- "no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"),
- "upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling"),
- "upcast_attn": OptionInfo(False, "Enable upcast cross attention layer"),
- "disable_nan_check": OptionInfo(True, "Disable NaN check in produced images/latent spaces"),
- "rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"),
- "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
- "cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"),
- "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
- "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
- "cuda_compile": OptionInfo(False, "Enable model compile (experimental)"),
- "cuda_compile_mode": OptionInfo("none", "Model compile mode (experimental)", gr.Radio, lambda: {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet', 'ipex']}),
- "cuda_compile_verbose": OptionInfo(False, "Model compile verbose mode"),
- "cuda_compile_errors": OptionInfo(True, "Model compile suppress errors"),
- "disable_gc": OptionInfo(False, "Disable Torch memory garbage collection (experimental)"),
-}))
-
-options_templates.update(options_section(('system-paths', "System Paths"), {
- "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"),
- "clean_temp_dir_at_start": OptionInfo(True, "Cleanup non-default temporary directory when starting webui"),
- "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Path to directory with stable diffusion checkpoints"),
- "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Path to directory with stable diffusion diffusers"),
- "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"),
- "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Path to directory with Lora network(s)"),
- "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Path to directory with LyCORIS network(s)"),
- "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "Path to user-defined styles file"),
- "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"),
- "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"),
- "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Path to directory with codeformer model file(s)"),
- "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Path to directory with GFPGAN model file(s)"),
- "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Path to directory with ESRGAN model file(s)"),
- "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Path to directory with BSRGAN model file(s)"),
- "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Path to directory with RealESRGAN model file(s)"),
- "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Path to directory with ScuNET model file(s)"),
- "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Path to directory with SwinIR model file(s)"),
- "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Path to directory with LDSR model file(s)"),
- "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Path to directory with CLIP model file(s)"),
-}))
-
-options_templates.update(options_section(('saving-images', "Image Options"), {
- "samples_save": OptionInfo(True, "Always save all generated images"),
- "samples_format": OptionInfo('jpg', 'File format for generated images', gr.Dropdown, lambda: {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}),
- "samples_filename_pattern": OptionInfo("[seed]-[prompt_spaces]", "Images filename pattern", component_args=hide_dirs),
- "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs),
- "grid_save": OptionInfo(True, "Always save all generated image grids"),
- "grid_format": OptionInfo('jpg', 'File format for grids', gr.Dropdown, lambda: {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}),
- "grid_extended_filename": OptionInfo(True, "Add extended info (seed, prompt) to filename when saving grid"),
- "grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
- "grid_prevent_empty_spots": OptionInfo(True, "Prevent empty spots in grid (when set to autodetect)"),
- "n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
- "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters"),
- "save_log_fn": OptionInfo("", "Create a JSON log file with image information for each saved image", component_args=hide_dirs),
- "save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration"),
- "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix"),
- "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"),
- "save_mask": OptionInfo(False, "Save a copy of the inpainting greyscale mask"),
- "save_mask_composite": OptionInfo(False, "Save a copy of inpainting masked composite"),
- "save_init_img": OptionInfo(False, "Save a copy of processing init images"),
- "jpeg_quality": OptionInfo(85, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}),
- "webp_lossless": OptionInfo(False, "Use lossless compression for webp images"),
- "img_max_size_mp": OptionInfo(250, "Maximum allowed image size in megapixels", gr.Number),
- "use_original_name_batch": OptionInfo(True, "Use original name for output filename during batch process in extras tab"),
- "use_upscaler_name_as_suffix": OptionInfo(True, "Use upscaler name as filename suffix in the extras tab"),
- "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"),
- "save_to_dirs": OptionInfo(False, "Save images to a subdirectory"),
- "grid_save_to_dirs": OptionInfo(False, "Save grids to a subdirectory"),
- "use_save_to_dirs_for_ui": OptionInfo(False, "Save images to a subdirectory when using Save button"),
- "directories_filename_pattern": OptionInfo("[date]", "Directory name pattern", component_args=hide_dirs),
- "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1, **hide_dirs}),
-}))
-
-options_templates.update(options_section(('image-processing', "Image Processing"), {
- "img2img_color_correction": OptionInfo(False, "Apply color correction to match original colors"),
- "img2img_fix_steps": OptionInfo(False, "For image processing do exact number of steps as specified"),
- "img2img_background_color": OptionInfo("#ffffff", "Image transparent color fill", ui_components.FormColorPicker, {}),
- "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for image processing", gr.Slider, {"minimum": 0.1, "maximum": 1.5, "step": 0.01}),
- "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1, "visible": False}),
-}))
-
-
-options_templates.update(options_section(('saving-paths', "Output Paths"), {
- "outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs),
- "outdir_txt2img_samples": OptionInfo("outputs/text", 'Output directory for txt2img images', component_args=hide_dirs),
- "outdir_img2img_samples": OptionInfo("outputs/image", 'Output directory for img2img images', component_args=hide_dirs),
- "outdir_extras_samples": OptionInfo("outputs/extras", 'Output directory for images from extras tab', component_args=hide_dirs),
- "outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs),
- "outdir_txt2img_grids": OptionInfo("outputs/grids", 'Output directory for txt2img grids', component_args=hide_dirs),
- "outdir_img2img_grids": OptionInfo("outputs/grids", 'Output directory for img2img grids', component_args=hide_dirs),
- "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs),
- "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs),
-}))
-
-options_templates.update(options_section(('ui', "User interface"), {
- "gradio_theme": OptionInfo("black-orange", "UI theme", gr.Dropdown, lambda: {"choices": list_themes()}, refresh=refresh_themes),
- "theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
- "return_grid": OptionInfo(True, "Show grid in results for web"),
- "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
- "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
- "disable_weights_auto_swap": OptionInfo(True, "Do not change the selected model when reading generation parameters"),
- "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
- "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
- "font": OptionInfo("", "Font for image grids that have text"),
- "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
- "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
- "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string
- "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
- "hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": [x for x in tab_names]}),
- "ui_tab_reorder": OptionInfo("From Text, From Image, Process Image", "UI tabs order"),
- "ui_scripts_reorder": OptionInfo("Enable Dynamic Thresholding, ControlNet", "UI scripts order"),
- "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"),
- "ui_extra_networks_tab_reorder": OptionInfo("Checkpoints, Lora, LyCORIS, Textual Inversion, Hypernetworks", "Extra networks tab order"),
-}))
-
-options_templates.update(options_section(('live-preview', "Live previews"), {
- "show_progressbar": OptionInfo(True, "Show progressbar"),
- "live_previews_enable": OptionInfo(True, "Show live previews of the created image"),
- "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"),
- "notification_audio_enable": OptionInfo(False, "Play a sound when images are finished generating"),
- "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs),
- "show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}),
- "show_progress_type": OptionInfo("TAESD", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}),
- "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}),
- "live_preview_refresh_period": OptionInfo(250, "Progressbar/preview update period, in milliseconds")
-}))
-
-options_templates.update(options_section(('sampler-params', "Sampler Settings"), {
- "show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ 2M SDE", "DPM++ 2M SDE Karras", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers() if x.name != "PLMS"]}),
- "fallback_sampler": OptionInfo("Euler a", "Secondary sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
- "force_latent_sampler": OptionInfo("None", "Force latent upscaler sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
- "eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- "eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- "ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}),
- 's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- 's_min_uncond': OptionInfo(0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 4.0, "step": 0.01}),
- 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- 'eta_noise_seed_delta': OptionInfo(0, "Noise seed delta (eta)", gr.Number, {"precision": 0}),
- 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma"),
- 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}),
- 'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}),
- 'uni_pc_order': OptionInfo(3, "UniPC order (must be < sampling steps)", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}),
- 'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final"),
-}))
-
-options_templates.update(options_section(('postprocessing', "Postprocessing"), {
- 'postprocessing_enable_in_main_ui': OptionInfo([], "Enable addtional postprocessing operations", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
- 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
- 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
-}))
-
-options_templates.update(options_section(('training', "Training"), {
- "unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible"),
- "pin_memory": OptionInfo(True, "Pin training dataset to memory"),
- "save_optimizer_state": OptionInfo(False, "Saves resumable optimizer state when training embedding or hypernetwork"),
- "save_training_settings_to_txt": OptionInfo(True, "Save textual inversion and hypernet settings to a text file whenever training starts"),
- "dataset_filename_word_regex": OptionInfo("", "Filename word regex"),
- "dataset_filename_join_string": OptionInfo(" ", "Filename join string"),
- "embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train', 'templates'), "Embeddings train templates directory"),
- "training_image_repeats_per_epoch": OptionInfo(1, "Number of repeats for a single input image per epoch; used only for displaying epoch number", gr.Number, {"precision": 0}),
- "training_write_csv_every": OptionInfo(0, "Save an csv containing the loss to log directory every N steps, 0 to disable"),
- "training_enable_tensorboard": OptionInfo(False, "Enable tensorboard logging"),
- "training_tensorboard_save_images": OptionInfo(False, "Save generated images within tensorboard"),
- "training_tensorboard_flush_every": OptionInfo(120, "How often, in seconds, to flush the pending tensorboard events and summaries to disk"),
-}))
-
-options_templates.update(options_section(('interrogate', "Interrogate"), {
- "interrogate_keep_models_in_memory": OptionInfo(False, "Interrogate: keep models in VRAM"),
- "interrogate_return_ranks": OptionInfo(True, "Interrogate: include ranks of model tags matches in results"),
- "interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
- "interrogate_clip_min_length": OptionInfo(32, "Interrogate: minimum description length (excluding artists, etc..)", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
- "interrogate_clip_max_length": OptionInfo(192, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
- "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file (0 = No limit)"),
- "interrogate_clip_skip_categories": OptionInfo(["artists", "movements", "flavors"], "CLIP: skip inquire categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types),
- "interrogate_deepbooru_score_threshold": OptionInfo(0.65, "Interrogate: deepbooru score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
- "deepbooru_sort_alpha": OptionInfo(False, "Interrogate: deepbooru sort alphabetically"),
- "deepbooru_use_spaces": OptionInfo(False, "use spaces for tags in deepbooru"),
- "deepbooru_escape": OptionInfo(True, "escape (\\) brackets in deepbooru (so they are used as literal brackets and not for emphasis)"),
- "deepbooru_filter_tags": OptionInfo("", "filter out those tags from deepbooru output (separated by comma)"),
-}))
-
-options_templates.update(options_section(('upscaling', "Upscaling"), {
- "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
- "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
- "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
- "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
- "SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
- "SCUNET_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SCUNET upscalers (low values = visible seam)", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}),
- "use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution rather than first pass"),
- "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"),
-}))
-
-options_templates.update(options_section(('lora', "Lora"), {
- "lyco_patch_lora": OptionInfo(False, "Use LyCoris handler for all Lora types", gr.Checkbox, { "visible": True }),
- "lora_disable": OptionInfo(False, "Disable built-in Lora handler", gr.Checkbox, { "visible": True }, onchange=lora_disable),
- "lora_functional": OptionInfo(False, "Use Kohya method for handling multiple Loras", gr.Checkbox, { "visible": True }),
-}))
-
-options_templates.update(options_section(('face-restoration', "Face restoration"), {
- "face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
- "code_former_weight": OptionInfo(0.2, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
- "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
-}))
-
-options_templates.update(options_section(('extra_networks', "Extra Networks"), {
- "extra_networks_default_view": OptionInfo("cards", "Default view for Extra Networks", gr.Dropdown, {"choices": ["cards", "thumbs"]}),
- "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
- "extra_networks_card_width": OptionInfo(0, "Card width for Extra Networks (px)"),
- "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks (px)"),
- "extra_networks_add_text_separator": OptionInfo(" ", "Extra text to add before <...> when adding extra network to prompt"),
- "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None"] + [x for x in hypernetworks.keys()]}, refresh=reload_hypernetworks),
-}))
-
-options_templates.update(options_section(('token_merging', 'Token Merging'), {
- "token_merging": OptionInfo(False, "Enable redundant token merging via tomesd for speed and memory improvements", gr.Checkbox),
- "token_merging_ratio": OptionInfo(0.5, "Token merging Ratio. Higher merging ratio = faster generation, smaller VRAM usage, lower quality", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
- "token_merging_hr_only": OptionInfo(True, "Apply only to high-res fix pass. Disabling can yield a ~20-35% speedup on contemporary resolutions", gr.Checkbox),
- "token_merging_ratio_hr": OptionInfo(0.5, "Merging Ratio for high-res pass", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
- "token_merging_random": OptionInfo(False, "Use random perturbations - Can improve outputs for certain samplers. For others, it may cause visual artifacting", gr.Checkbox),
- "token_merging_merge_attention": OptionInfo(True, "Merge attention (Recommend on)", gr.Checkbox),
- "token_merging_merge_cross_attention": OptionInfo(False, "Merge cross attention (Recommend off)", gr.Checkbox),
- "token_merging_merge_mlp": OptionInfo(False, "Merge mlp (Strongly recommend off)", gr.Checkbox),
- "token_merging_maximum_down_sampling": OptionInfo(1, "Maximum down sampling", gr.Radio, lambda: {"choices": [1, 2, 4, 8]}),
- "token_merging_stride_x": OptionInfo(2, "Stride - X", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2}),
- "token_merging_stride_y": OptionInfo(2, "Stride - Y", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2})
-}))
-
-options_templates.update(options_section((None, "Hidden options"), {
- "disabled_extensions": OptionInfo([], "Disable these extensions"),
- "disable_all_extensions": OptionInfo("none", "Disable all extensions (preserves the list of disabled extensions)", gr.Radio, {"choices": ["none", "user", "all"]}),
- "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"),
-}))
-
-options_templates.update()
-
-
-class Options:
- data = None
- data_labels = options_templates
- typemap = {int: float}
-
- def __init__(self):
- self.data = {k: v.default for k, v in self.data_labels.items()}
-
- def __setattr__(self, key, value):
- if self.data is not None:
- if key in self.data or key in self.data_labels:
- if cmd_opts.freeze:
- log.warning(f'Settings are frozen: {key}')
- return
- if cmd_opts.hide_ui_dir_config and key in restricted_opts:
- log.warning(f'Settings key is restricted: {key}')
- return
- else:
- self.data[key] = value
- return
-
- return super(Options, self).__setattr__(key, value)
-
- def __getattr__(self, item):
- if self.data is not None:
- if item in self.data:
- return self.data[item]
- if item in self.data_labels:
- return self.data_labels[item].default
- return super(Options, self).__getattribute__(item)
-
- def set(self, key, value):
- """sets an option and calls its onchange callback, returning True if the option changed and False otherwise"""
- oldval = self.data.get(key, None)
- if oldval == value:
- return False
- try:
- setattr(self, key, value)
- except RuntimeError:
- return False
- if self.data_labels[key].onchange is not None:
- try:
- self.data_labels[key].onchange()
- except Exception as e:
- errors.display(e, f"changing setting {key} to {value}")
- setattr(self, key, oldval)
- return False
- return True
-
- def get_default(self, key):
- """returns the default value for the key"""
- data_label = self.data_labels.get(key)
- if data_label is None:
- return None
- return data_label.default
-
- def save(self, filename):
- if cmd_opts.freeze:
- log.warning(f'Settings saving is disabled: {filename}')
- return
- with open(filename, "w", encoding="utf8") as file:
- json.dump(self.data, file, indent=4)
-
- def same_type(self, x, y):
- if x is None or y is None:
- return True
- type_x = self.typemap.get(type(x), type(x))
- type_y = self.typemap.get(type(y), type(y))
- return type_x == type_y
-
- def load(self, filename):
- if not os.path.isfile(filename):
- log.debug(f'Created default config: {filename}')
- self.save(filename)
- return
- with open(filename, "r", encoding="utf8") as file:
- self.data = json.load(file)
- if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
- self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
- bad_settings = 0
- for k, v in self.data.items():
- info = self.data_labels.get(k, None)
- if info is not None and not self.same_type(info.default, v):
- log.error(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})")
- bad_settings += 1
- if bad_settings > 0:
- log.error(f"Error: Bad settings found in {filename}")
-
- def onchange(self, key, func, call=True):
- item = self.data_labels.get(key)
- item.onchange = func
- if call:
- func()
-
- def dumpjson(self):
- d = {k: self.data.get(k, self.data_labels.get(k).default) for k in self.data_labels.keys()}
- metadata = {
- k: {
- "is_stored": k in self.data,
- "tab_name": v.section[0]
- } for k, v in self.data_labels.items()
- }
- return json.dumps({"values": d, "metadata": metadata})
-
- def add_option(self, key, info):
- self.data_labels[key] = info
-
- def reorder(self):
- """reorder settings so that all items related to section always go together"""
- section_ids = {}
- settings_items = self.data_labels.items()
- for k, item in settings_items:
- if item.section not in section_ids:
- section_ids[item.section] = len(section_ids)
- self.data_labels = {k: v for k, v in sorted(settings_items, key=lambda x: section_ids[x[1].section])}
-
- def cast_value(self, key, value):
- """casts an arbitrary to the same type as this setting's value with key
- Example: cast_value("eta_noise_seed_delta", "12") -> returns 12 (an int rather than str)
- """
- if value is None:
- return None
- default_value = self.data_labels[key].default
- if default_value is None:
- default_value = getattr(self, key, None)
- if default_value is None:
- return None
- expected_type = type(default_value)
- if expected_type == bool and value == "False":
- value = False
- elif expected_type == type(value):
- pass
- else:
- value = expected_type(value)
- return value
-
-
-opts = Options()
-config_filename = cmd_opts.config
-opts.load(config_filename)
-cmd_opts = cmd_args.compatibility_args(opts, cmd_opts)
-if cmd_opts.backend == 'diffusers':
- log.info('Overriding backend to Diffusers')
- opts.data['sd_backend'] = 'Diffusers'
-if cmd_opts.backend == 'original':
- log.info('Overriding backend to Diffusers')
- opts.data['sd_backend'] = 'Original'
-backend = Backend.DIFFUSERS if opts.sd_backend == 'Diffusers' else Backend.ORIGINAL
-
-prompt_styles = modules.styles.StyleDatabase(opts.styles_dir)
-cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or (cmd_opts.server_name or False)) and not cmd_opts.insecure
-devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
-device = devices.device
-batch_cond_uncond = opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram)
-parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram
-mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts)
-mem_mon.start()
-if device.type == 'privateuseone':
- import modules.dml # pylint: disable=ungrouped-imports
-
-
-def reload_gradio_theme(theme_name=None):
- global gradio_theme # pylint: disable=global-statement
- if not theme_name:
- theme_name = opts.gradio_theme
- default_font_params = {}
- res = 0
- try:
- req = urllib.request.Request("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono", method="HEAD")
- res = urllib.request.urlopen(req, timeout=3.0).status
- except:
- res = 0
- if res != 200:
- log.info('No internet access detected, using default fonts')
- default_font_params = {
- 'font':['Helvetica', 'ui-sans-serif', 'system-ui', 'sans-serif'],
- 'font_mono':['IBM Plex Mono', 'ui-monospace', 'Consolas', 'monospace']
- }
- if theme_name == "black-orange":
- gradio_theme = gr.themes.Default(**default_font_params)
- elif theme_name.startswith("gradio/"):
- if theme_name == "gradio/default":
- gradio_theme = gr.themes.Default(**default_font_params)
- if theme_name == "gradio/base":
- gradio_theme = gr.themes.Base(**default_font_params)
- if theme_name == "gradio/glass":
- gradio_theme = gr.themes.Glass(**default_font_params)
- if theme_name == "gradio/monochrome":
- gradio_theme = gr.themes.Monochrome(**default_font_params)
- if theme_name == "gradio/soft":
- gradio_theme = gr.themes.Soft(**default_font_params)
- else:
- try:
- gradio_theme = gr.themes.ThemeClass.from_hub(theme_name)
- except:
- log.error("Theme download error accessing HuggingFace")
- gradio_theme = gr.themes.Default(**default_font_params)
- log.info(f'Loading UI theme: name={theme_name} style={opts.theme_style}')
-
-
-class TotalTQDM:
- def __init__(self):
- self._tqdm = None
-
- def reset(self):
- self._tqdm = tqdm.tqdm(
- desc="Total",
- total=state.job_count * state.sampling_steps,
- position=1,
- )
-
- def update(self):
- if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
- return
- if self._tqdm is None:
- self.reset()
- self._tqdm.update()
-
- def updateTotal(self, new_total):
- if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
- return
- if self._tqdm is None:
- self.reset()
- self._tqdm.total = new_total
-
- def clear(self):
- if self._tqdm is not None:
- self._tqdm.refresh()
- self._tqdm.close()
- self._tqdm = None
-
-total_tqdm = TotalTQDM()
-
-
-def restart_server(restart=True):
- if demo is None:
- return
- log.info('Server shutdown requested')
- try:
- demo.server.wants_restart = restart
- demo.server.should_exit = True
- demo.server.force_exit = True
- demo.close(verbose=False)
- demo.server.close()
- demo.fns = []
- except:
- pass
- if restart:
- log.info('Server will restart')
-
-
-def restore_defaults(restart=True):
- if os.path.exists(cmd_opts.config):
- log.info('Restoring server defaults')
- os.remove(cmd_opts.config)
- if os.path.exists(cmd_opts.ui_config):
- log.info('Restoring UI defaults')
- os.remove(cmd_opts.ui_config)
- restart_server(restart)
-
-
-def listfiles(dirname):
- filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=str.lower) if not x.startswith(".")]
- return [file for file in filenames if os.path.isfile(file)]
-
-
-def walk_files(path, allowed_extensions=None):
- if not os.path.exists(path):
- return
- if allowed_extensions is not None:
- allowed_extensions = set(allowed_extensions)
- for root, _dirs, files in os.walk(path, followlinks=True):
- for filename in files:
- if allowed_extensions is not None:
- _, ext = os.path.splitext(filename)
- if ext not in allowed_extensions:
- continue
- yield os.path.join(root, filename)
-
-
-def html_path(filename):
- return os.path.join(paths.script_path, "html", filename)
-
-
-def html(filename):
- path = html_path(filename)
- if os.path.exists(path):
- with open(path, encoding="utf8") as file:
- return file.read()
- return ""
-
-
-def get_version():
- version = None
- if version is None:
- try:
- import subprocess
- res = subprocess.run('git log --pretty=format:"%h %ad" -1 --date=short', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
- ver = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ' '
- githash, updated = ver.split(' ')
- res = subprocess.run('git remote get-url origin', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
- origin = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ''
- res = subprocess.run('git branch --show-current', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
- branch = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ''
- version = {
- 'app': 'sd.next',
- 'updated': updated,
- 'hash': githash,
- 'url': origin.replace('\n', '') + '/tree/' + branch.replace('\n', '')
- }
- except:
- version = { 'app': 'sd.next' }
- return version
-
-
-class Shared(sys.modules[__name__].__class__):
- # this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than at program startup.
- sd_model_val = None
-
- @property
- def sd_model(self):
- import modules.sd_models # pylint: disable=W0621
- # return modules.sd_models.model_data.sd_model
- return modules.sd_models.model_data.get_sd_model()
-
- @sd_model.setter
- def sd_model(self, value):
- import modules.sd_models # pylint: disable=W0621
- modules.sd_models.model_data.set_sd_model(value)
-
-# sd_model: LatentDiffusion = None # this var is here just for IDE's type checking; it cannot be accessed because the class field above will be accessed instead
-sd_model = None
-sys.modules[__name__].__class__ = Shared
+import os
+import sys
+import time
+import json
+import datetime
+import urllib.request
+from enum import Enum
+import gradio as gr
+import tqdm
+import requests
+from modules import errors, ui_components, shared_items, cmd_args
+from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir # pylint: disable=W0611
+import modules.interrogate
+import modules.memmon
+import modules.styles
+import modules.devices as devices
+import modules.paths_internal as paths
+from installer import log as central_logger # pylint: disable=E0611
+
+
+errors.install(gr)
+demo: gr.Blocks = None
+log = central_logger
+progress_print_out = sys.stdout
+parser = cmd_args.parser
+url = 'https://github.com/vladmandic/automatic'
+cmd_opts, _ = parser.parse_known_args()
+hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
+xformers_available = False
+clip_model = None
+interrogator = modules.interrogate.InterrogateModels("interrogate")
+sd_upscalers = []
+face_restorers = []
+tab_names = []
+options_templates = {}
+hypernetworks = {}
+loaded_hypernetworks = []
+gradio_theme = gr.themes.Base()
+settings_components = None
+latent_upscale_default_mode = "Latent"
+latent_upscale_modes = {
+ "Latent": {"mode": "bilinear", "antialias": False},
+ "Latent (antialiased)": {"mode": "bilinear", "antialias": True},
+ "Latent (bicubic)": {"mode": "bicubic", "antialias": False},
+ "Latent (bicubic antialiased)": {"mode": "bicubic", "antialias": True},
+ "Latent (nearest)": {"mode": "nearest", "antialias": False},
+ "Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False},
+}
+restricted_opts = {
+ "samples_filename_pattern",
+ "directories_filename_pattern",
+ "outdir_samples",
+ "outdir_txt2img_samples",
+ "outdir_img2img_samples",
+ "outdir_extras_samples",
+ "outdir_grids",
+ "outdir_txt2img_grids",
+ "outdir_save",
+ "outdir_init_images"
+}
+ui_reorder_categories = [
+ "inpaint",
+ "sampler",
+ "checkboxes",
+ "hires_fix",
+ "dimensions",
+ "cfg",
+ "seed",
+ "batch",
+ "override_settings",
+ "scripts",
+]
+
+
+class Backend(Enum):
+ ORIGINAL = 1
+ DIFFUSERS = 2
+
+
+def reload_hypernetworks():
+ from modules.hypernetworks import hypernetwork
+ global hypernetworks # pylint: disable=W0603
+ hypernetworks = hypernetwork.list_hypernetworks(opts.hypernetwork_dir)
+
+
+class State:
+ skipped = False
+ interrupted = False
+ paused = False
+ job = ""
+ job_no = 0
+ job_count = 0
+ processing_has_refined_job_count = False
+ job_timestamp = '0'
+ sampling_step = 0
+ sampling_steps = 0
+ current_latent = None
+ current_image = None
+ current_image_sampling_step = 0
+ id_live_preview = 0
+ textinfo = None
+ time_start = None
+ need_restart = False
+ server_start = None
+ oom = False
+
+ def skip(self):
+ log.debug('Requested skip')
+ self.skipped = True
+
+ def interrupt(self):
+ log.debug('Requested interrupt')
+ self.interrupted = True
+
+ def pause(self):
+ self.paused = not self.paused
+ log.debug(f'Requested {"pause" if self.paused else "continue"}')
+
+ def nextjob(self):
+ if opts.live_previews_enable and opts.show_progress_every_n_steps == -1:
+ self.do_set_current_image()
+ self.job_no += 1
+ self.sampling_step = 0
+ self.current_image_sampling_step = 0
+
+ def dict(self):
+ obj = {
+ "skipped": self.skipped,
+ "interrupted": self.interrupted,
+ "job": self.job,
+ "job_count": self.job_count,
+ "job_timestamp": self.job_timestamp,
+ "job_no": self.job_no,
+ "sampling_step": self.sampling_step,
+ "sampling_steps": self.sampling_steps,
+ }
+ return obj
+
+ def begin(self):
+ self.sampling_step = 0
+ self.job_count = -1
+ self.processing_has_refined_job_count = False
+ self.job_no = 0
+ self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
+ self.current_latent = None
+ self.current_image = None
+ self.current_image_sampling_step = 0
+ self.id_live_preview = 0
+ self.skipped = False
+ self.interrupted = False
+ self.paused = False
+ self.textinfo = None
+ self.time_start = time.time()
+ devices.torch_gc()
+
+ def end(self):
+ self.job = ""
+ self.job_count = 0
+ self.paused = False
+ devices.torch_gc()
+
+ def set_current_image(self):
+ """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this"""
+ if not parallel_processing_allowed:
+ return
+ if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.live_previews_enable and opts.show_progress_every_n_steps != -1:
+ self.do_set_current_image()
+
+ def do_set_current_image(self):
+ if self.current_latent is None:
+ return
+ import modules.sd_samplers # pylint: disable=W0621
+ if opts.show_progress_grid:
+ self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent))
+ else:
+ self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent))
+ self.current_image_sampling_step = self.sampling_step
+
+ def assign_current_image(self, image):
+ self.current_image = image
+ self.id_live_preview += 1
+
+state = State()
+state.server_start = time.time()
+
+
+class OptionInfo:
+ def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, comment_before='', comment_after=''):
+ self.default = default
+ self.label = label
+ self.component = component
+ self.component_args = component_args
+ self.onchange = onchange
+ self.section = section
+ self.refresh = refresh
+ self.comment_before = comment_before # HTML text that will be added after label in UI
+ self.comment_after = comment_after # HTML text that will be added before label in UI
+
+ def link(self, label, uri):
+ self.comment_before += f"[{label}]"
+ return self
+
+ def js(self, label, js_func):
+ self.comment_before += f"[{label}]"
+ return self
+
+ def info(self, info):
+ self.comment_after += f"({info})"
+ return self
+
+ def needs_restart(self):
+ self.comment_after += " (requires restart)"
+ return self
+
+
+def options_section(section_identifier, options_dict):
+ for v in options_dict.values():
+ v.section = section_identifier
+ return options_dict
+
+
+def list_checkpoint_tiles():
+ import modules.sd_models # pylint: disable=W0621
+ return modules.sd_models.checkpoint_tiles()
+
+
+default_checkpoint = list_checkpoint_tiles()[0] if len(list_checkpoint_tiles()) > 0 else "model.ckpt"
+
+
+def refresh_checkpoints():
+ import modules.sd_models # pylint: disable=W0621
+ return modules.sd_models.list_models()
+
+
+def list_samplers():
+ import modules.sd_samplers # pylint: disable=W0621
+ modules.sd_samplers.set_samplers()
+ return modules.sd_samplers.all_samplers
+
+def list_themes():
+ fn = os.path.join('html', 'themes.json')
+ if not os.path.exists(fn):
+ refresh_themes()
+ if os.path.exists(fn):
+ with open(fn, mode='r', encoding='utf=8') as f:
+ res = json.loads(f.read())
+ else:
+ res = []
+ builtin = ["black-orange", "gradio/default", "gradio/base", "gradio/glass", "gradio/monochrome", "gradio/soft"]
+ themes = sorted(set(builtin + [x['id'] for x in res if x['status'] == 'RUNNING' and 'test' not in x['id'].lower()]), key=str.casefold)
+ return themes
+
+
+def lora_disable():
+ if opts.lora_disable:
+ if 'Lora' not in opts.disabled_extensions:
+ opts.data['disabled_extensions'].append('Lora')
+ else:
+ opts.data['disabled_extensions'] = [x for x in opts.disabled_extensions if x != 'Lora']
+
+
+def refresh_themes():
+ try:
+ req = requests.get('https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/subdomains.json', timeout=5)
+ if req.status_code == 200:
+ res = req.json()
+ fn = os.path.join('html', 'themes.json')
+ with open(fn, mode='w', encoding='utf=8') as f:
+ f.write(json.dumps(res))
+ else:
+ log.error('Error refreshing UI themes')
+ except:
+ log.error('Exception refreshing UI themes')
+
+
+if devices.backend == "cpu":
+ cross_attention_optimization_default = "Doggettx's"
+elif devices.backend == "mps":
+ cross_attention_optimization_default = "Doggettx's"
+elif devices.backend == "ipex":
+ cross_attention_optimization_default = "InvokeAI's"
+elif devices.backend == "directml":
+ cross_attention_optimization_default = "Sub-quadratic"
+elif devices.backend == "rocm":
+ cross_attention_optimization_default = "Sub-quadratic"
+else: # cuda
+ cross_attention_optimization_default ="Scaled-Dot-Product"
+
+options_templates.update(options_section(('sd', "Stable Diffusion"), {
+ "sd_model_checkpoint": OptionInfo(default_checkpoint, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
+ "sd_checkpoint_cache": OptionInfo(0, "Number of cached model checkpoints", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
+ "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAE checkpoints", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
+ "sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
+ "sd_model_dict": OptionInfo('None', "Stable Diffusion checkpoint dict", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
+ "sd_vae_sliced_encode": OptionInfo(False, "Enable splitting of hires batch processing"),
+ "stream_load": OptionInfo(False, "When loading models attempt stream loading optimized for slow or network storage"),
+ "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"),
+ "cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
+ "cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
+ "sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
+ "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
+ "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
+ "prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }),
+ "prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"),
+ "always_batch_cond_uncond": OptionInfo(False, "Disable conditional batching enabled on low memory systems"),
+ "enable_quantization": OptionInfo(True, "Enable samplers quantization for sharper and cleaner results"),
+ "comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
+ "sd_backend": OptionInfo("Original", "Stable Diffusion backend (experimental)", gr.Radio, lambda: {"choices": ["Original", "Diffusers"] }),
+}))
+
+options_templates.update(options_section(('cuda', "Compute Settings"), {
+ "memmon_poll_rate": OptionInfo(2, "VRAM usage polls per second during generation", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
+ "precision": OptionInfo("Autocast", "Precision type", gr.Radio, lambda: {"choices": ["Autocast", "Full"]}),
+ "cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" else "FP16", "Device precision type", gr.Radio, lambda: {"choices": ["FP32", "FP16", "BF16"]}),
+ "no_half": OptionInfo(False, "Use full precision for model (--no-half)", None, None, None),
+ "no_half_vae": OptionInfo(False, "Use full precision for VAE (--no-half-vae)"),
+ "upcast_sampling": OptionInfo(True if sys.platform == "darwin" else False, "Enable upcast sampling"),
+ "upcast_attn": OptionInfo(False, "Enable upcast cross attention layer"),
+ "disable_nan_check": OptionInfo(True, "Disable NaN check in produced images/latent spaces"),
+ "rollback_vae": OptionInfo(False, "Attempt to roll back VAE when produced NaN values, requires NaN check (experimental)"),
+ "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "),
+ "cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"),
+ "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"),
+ "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"),
+ "cuda_compile": OptionInfo(False, "Enable model compile (experimental)"),
+ "cuda_compile_mode": OptionInfo("none", "Model compile mode (experimental)", gr.Radio, lambda: {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet', 'ipex']}),
+ "cuda_compile_verbose": OptionInfo(False, "Model compile verbose mode"),
+ "cuda_compile_errors": OptionInfo(True, "Model compile suppress errors"),
+ "disable_gc": OptionInfo(False, "Disable Torch memory garbage collection (experimental)"),
+}))
+
+options_templates.update(options_section(('system-paths', "System Paths"), {
+ "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"),
+ "clean_temp_dir_at_start": OptionInfo(True, "Cleanup non-default temporary directory when starting webui"),
+ "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Path to directory with stable diffusion checkpoints"),
+ "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Path to directory with stable diffusion diffusers"),
+ "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Path to directory with VAE files"),
+ "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Path to directory with Lora network(s)"),
+ "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Path to directory with LyCORIS network(s)"),
+ "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "Path to user-defined styles file"),
+ "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Embeddings directory for textual inversion"),
+ "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Hypernetwork directory"),
+ "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Path to directory with codeformer model file(s)"),
+ "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Path to directory with GFPGAN model file(s)"),
+ "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Path to directory with ESRGAN model file(s)"),
+ "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Path to directory with BSRGAN model file(s)"),
+ "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Path to directory with RealESRGAN model file(s)"),
+ "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Path to directory with ScuNET model file(s)"),
+ "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Path to directory with SwinIR model file(s)"),
+ "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Path to directory with LDSR model file(s)"),
+ "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Path to directory with CLIP model file(s)"),
+}))
+
+options_templates.update(options_section(('saving-images', "Image Options"), {
+ "samples_save": OptionInfo(True, "Always save all generated images"),
+ "samples_format": OptionInfo('jpg', 'File format for generated images', gr.Dropdown, lambda: {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}),
+ "samples_filename_pattern": OptionInfo("[seed]-[prompt_spaces]", "Images filename pattern", component_args=hide_dirs),
+ "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs),
+ "grid_save": OptionInfo(True, "Always save all generated image grids"),
+ "grid_format": OptionInfo('jpg', 'File format for grids', gr.Dropdown, lambda: {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}),
+ "grid_extended_filename": OptionInfo(True, "Add extended info (seed, prompt) to filename when saving grid"),
+ "grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
+ "grid_prevent_empty_spots": OptionInfo(True, "Prevent empty spots in grid (when set to autodetect)"),
+ "n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
+ "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters"),
+ "save_log_fn": OptionInfo("", "Create a JSON log file with image information for each saved image", component_args=hide_dirs),
+ "save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration"),
+ "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix"),
+ "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"),
+ "save_mask": OptionInfo(False, "Save a copy of the inpainting greyscale mask"),
+ "save_mask_composite": OptionInfo(False, "Save a copy of inpainting masked composite"),
+ "save_init_img": OptionInfo(False, "Save a copy of processing init images"),
+ "jpeg_quality": OptionInfo(85, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}),
+ "webp_lossless": OptionInfo(False, "Use lossless compression for webp images"),
+ "img_max_size_mp": OptionInfo(250, "Maximum allowed image size in megapixels", gr.Number),
+ "use_original_name_batch": OptionInfo(True, "Use original name for output filename during batch process in extras tab"),
+ "use_upscaler_name_as_suffix": OptionInfo(True, "Use upscaler name as filename suffix in the extras tab"),
+ "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"),
+ "save_to_dirs": OptionInfo(False, "Save images to a subdirectory"),
+ "grid_save_to_dirs": OptionInfo(False, "Save grids to a subdirectory"),
+ "use_save_to_dirs_for_ui": OptionInfo(False, "Save images to a subdirectory when using Save button"),
+ "directories_filename_pattern": OptionInfo("[date]", "Directory name pattern", component_args=hide_dirs),
+ "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1, **hide_dirs}),
+}))
+
+options_templates.update(options_section(('image-processing', "Image Processing"), {
+ "img2img_color_correction": OptionInfo(False, "Apply color correction to match original colors"),
+ "img2img_fix_steps": OptionInfo(False, "For image processing do exact number of steps as specified"),
+ "img2img_background_color": OptionInfo("#ffffff", "Image transparent color fill", ui_components.FormColorPicker, {}),
+ "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for image processing", gr.Slider, {"minimum": 0.1, "maximum": 1.5, "step": 0.01}),
+ "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1, "visible": False}),
+}))
+
+
+options_templates.update(options_section(('saving-paths', "Output Paths"), {
+ "outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs),
+ "outdir_txt2img_samples": OptionInfo("outputs/text", 'Output directory for txt2img images', component_args=hide_dirs),
+ "outdir_img2img_samples": OptionInfo("outputs/image", 'Output directory for img2img images', component_args=hide_dirs),
+ "outdir_extras_samples": OptionInfo("outputs/extras", 'Output directory for images from extras tab', component_args=hide_dirs),
+ "outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs),
+ "outdir_txt2img_grids": OptionInfo("outputs/grids", 'Output directory for txt2img grids', component_args=hide_dirs),
+ "outdir_img2img_grids": OptionInfo("outputs/grids", 'Output directory for img2img grids', component_args=hide_dirs),
+ "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs),
+ "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs),
+}))
+
+options_templates.update(options_section(('ui', "User interface"), {
+ "gradio_theme": OptionInfo("black-orange", "UI theme", gr.Dropdown, lambda: {"choices": list_themes()}, refresh=refresh_themes),
+ "theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
+ "return_grid": OptionInfo(True, "Show grid in results for web"),
+ "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
+ "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
+ "disable_weights_auto_swap": OptionInfo(True, "Do not change the selected model when reading generation parameters"),
+ "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
+ "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
+ "font": OptionInfo("", "Font for image grids that have text"),
+ "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
+ "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
+ "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string
+ "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}),
+ "hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": [x for x in tab_names]}),
+ "ui_tab_reorder": OptionInfo("From Text, From Image, Process Image", "UI tabs order"),
+ "ui_scripts_reorder": OptionInfo("Enable Dynamic Thresholding, ControlNet", "UI scripts order"),
+ "ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order"),
+ "ui_extra_networks_tab_reorder": OptionInfo("Checkpoints, Lora, LyCORIS, Textual Inversion, Hypernetworks", "Extra networks tab order"),
+}))
+
+options_templates.update(options_section(('live-preview', "Live previews"), {
+ "show_progressbar": OptionInfo(True, "Show progressbar"),
+ "live_previews_enable": OptionInfo(True, "Show live previews of the created image"),
+ "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"),
+ "notification_audio_enable": OptionInfo(False, "Play a sound when images are finished generating"),
+ "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs),
+ "show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}),
+ "show_progress_type": OptionInfo("TAESD", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}),
+ "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}),
+ "live_preview_refresh_period": OptionInfo(250, "Progressbar/preview update period, in milliseconds")
+}))
+
+options_templates.update(options_section(('sampler-params', "Sampler Settings"), {
+ "show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ 2M SDE", "DPM++ 2M SDE Karras", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers() if x.name != "PLMS"]}),
+ "fallback_sampler": OptionInfo("Euler a", "Secondary sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
+ "force_latent_sampler": OptionInfo("None", "Force latent upscaler sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
+ "eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ "eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ "ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}),
+ 's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ 's_min_uncond': OptionInfo(0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 4.0, "step": 0.01}),
+ 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ 'eta_noise_seed_delta': OptionInfo(0, "Noise seed delta (eta)", gr.Number, {"precision": 0}),
+ 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma"),
+ 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}),
+ 'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}),
+ 'uni_pc_order': OptionInfo(3, "UniPC order (must be < sampling steps)", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}),
+ 'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final"),
+}))
+
+options_templates.update(options_section(('postprocessing', "Postprocessing"), {
+ 'postprocessing_enable_in_main_ui': OptionInfo([], "Enable addtional postprocessing operations", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
+ 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
+ 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
+}))
+
+options_templates.update(options_section(('training', "Training"), {
+ "unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible"),
+ "pin_memory": OptionInfo(True, "Pin training dataset to memory"),
+ "save_optimizer_state": OptionInfo(False, "Saves resumable optimizer state when training embedding or hypernetwork"),
+ "save_training_settings_to_txt": OptionInfo(True, "Save textual inversion and hypernet settings to a text file whenever training starts"),
+ "dataset_filename_word_regex": OptionInfo("", "Filename word regex"),
+ "dataset_filename_join_string": OptionInfo(" ", "Filename join string"),
+ "embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train', 'templates'), "Embeddings train templates directory"),
+ "training_image_repeats_per_epoch": OptionInfo(1, "Number of repeats for a single input image per epoch; used only for displaying epoch number", gr.Number, {"precision": 0}),
+ "training_write_csv_every": OptionInfo(0, "Save an csv containing the loss to log directory every N steps, 0 to disable"),
+ "training_enable_tensorboard": OptionInfo(False, "Enable tensorboard logging"),
+ "training_tensorboard_save_images": OptionInfo(False, "Save generated images within tensorboard"),
+ "training_tensorboard_flush_every": OptionInfo(120, "How often, in seconds, to flush the pending tensorboard events and summaries to disk"),
+}))
+
+options_templates.update(options_section(('interrogate', "Interrogate"), {
+ "interrogate_keep_models_in_memory": OptionInfo(False, "Interrogate: keep models in VRAM"),
+ "interrogate_return_ranks": OptionInfo(True, "Interrogate: include ranks of model tags matches in results"),
+ "interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
+ "interrogate_clip_min_length": OptionInfo(32, "Interrogate: minimum description length (excluding artists, etc..)", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
+ "interrogate_clip_max_length": OptionInfo(192, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
+ "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file (0 = No limit)"),
+ "interrogate_clip_skip_categories": OptionInfo(["artists", "movements", "flavors"], "CLIP: skip inquire categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types),
+ "interrogate_deepbooru_score_threshold": OptionInfo(0.65, "Interrogate: deepbooru score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
+ "deepbooru_sort_alpha": OptionInfo(False, "Interrogate: deepbooru sort alphabetically"),
+ "deepbooru_use_spaces": OptionInfo(False, "use spaces for tags in deepbooru"),
+ "deepbooru_escape": OptionInfo(True, "escape (\\) brackets in deepbooru (so they are used as literal brackets and not for emphasis)"),
+ "deepbooru_filter_tags": OptionInfo("", "filter out those tags from deepbooru output (separated by comma)"),
+}))
+
+options_templates.update(options_section(('upscaling', "Upscaling"), {
+ "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
+ "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
+ "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
+ "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
+ "SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
+ "SCUNET_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SCUNET upscalers (low values = visible seam)", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}),
+ "use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution rather than first pass"),
+ "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"),
+}))
+
+options_templates.update(options_section(('lora', "Lora"), {
+ "lyco_patch_lora": OptionInfo(False, "Use LyCoris handler for all Lora types", gr.Checkbox, { "visible": True }),
+ "lora_disable": OptionInfo(False, "Disable built-in Lora handler", gr.Checkbox, { "visible": True }, onchange=lora_disable),
+ "lora_functional": OptionInfo(False, "Use Kohya method for handling multiple Loras", gr.Checkbox, { "visible": True }),
+}))
+
+options_templates.update(options_section(('face-restoration', "Face restoration"), {
+ "face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
+ "code_former_weight": OptionInfo(0.2, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
+ "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
+}))
+
+options_templates.update(options_section(('extra_networks', "Extra Networks"), {
+ "extra_networks_default_view": OptionInfo("cards", "Default view for Extra Networks", gr.Dropdown, {"choices": ["cards", "thumbs"]}),
+ "extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+ "extra_networks_card_width": OptionInfo(0, "Card width for Extra Networks (px)"),
+ "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks (px)"),
+ "extra_networks_add_text_separator": OptionInfo(" ", "Extra text to add before <...> when adding extra network to prompt"),
+ "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None"] + [x for x in hypernetworks.keys()]}, refresh=reload_hypernetworks),
+}))
+
+options_templates.update(options_section(('token_merging', 'Token Merging'), {
+ "token_merging": OptionInfo(False, "Enable redundant token merging via tomesd for speed and memory improvements", gr.Checkbox),
+ "token_merging_ratio": OptionInfo(0.5, "Token merging Ratio. Higher merging ratio = faster generation, smaller VRAM usage, lower quality", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
+ "token_merging_hr_only": OptionInfo(True, "Apply only to high-res fix pass. Disabling can yield a ~20-35% speedup on contemporary resolutions", gr.Checkbox),
+ "token_merging_ratio_hr": OptionInfo(0.5, "Merging Ratio for high-res pass", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
+ "token_merging_random": OptionInfo(False, "Use random perturbations - Can improve outputs for certain samplers. For others, it may cause visual artifacting", gr.Checkbox),
+ "token_merging_merge_attention": OptionInfo(True, "Merge attention (Recommend on)", gr.Checkbox),
+ "token_merging_merge_cross_attention": OptionInfo(False, "Merge cross attention (Recommend off)", gr.Checkbox),
+ "token_merging_merge_mlp": OptionInfo(False, "Merge mlp (Strongly recommend off)", gr.Checkbox),
+ "token_merging_maximum_down_sampling": OptionInfo(1, "Maximum down sampling", gr.Radio, lambda: {"choices": [1, 2, 4, 8]}),
+ "token_merging_stride_x": OptionInfo(2, "Stride - X", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2}),
+ "token_merging_stride_y": OptionInfo(2, "Stride - Y", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2})
+}))
+
+options_templates.update(options_section((None, "Hidden options"), {
+ "disabled_extensions": OptionInfo([], "Disable these extensions"),
+ "disable_all_extensions": OptionInfo("none", "Disable all extensions (preserves the list of disabled extensions)", gr.Radio, {"choices": ["none", "user", "all"]}),
+ "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"),
+}))
+
+options_templates.update()
+
+
+class Options:
+ data = None
+ data_labels = options_templates
+ typemap = {int: float}
+
+ def __init__(self):
+ self.data = {k: v.default for k, v in self.data_labels.items()}
+
+ def __setattr__(self, key, value):
+ if self.data is not None:
+ if key in self.data or key in self.data_labels:
+ if cmd_opts.freeze:
+ log.warning(f'Settings are frozen: {key}')
+ return
+ if cmd_opts.hide_ui_dir_config and key in restricted_opts:
+ log.warning(f'Settings key is restricted: {key}')
+ return
+ else:
+ self.data[key] = value
+ return
+
+ return super(Options, self).__setattr__(key, value)
+
+ def __getattr__(self, item):
+ if self.data is not None:
+ if item in self.data:
+ return self.data[item]
+ if item in self.data_labels:
+ return self.data_labels[item].default
+ return super(Options, self).__getattribute__(item)
+
+ def set(self, key, value):
+ """sets an option and calls its onchange callback, returning True if the option changed and False otherwise"""
+ oldval = self.data.get(key, None)
+ if oldval == value:
+ return False
+ try:
+ setattr(self, key, value)
+ except RuntimeError:
+ return False
+ if self.data_labels[key].onchange is not None:
+ try:
+ self.data_labels[key].onchange()
+ except Exception as e:
+ errors.display(e, f"changing setting {key} to {value}")
+ setattr(self, key, oldval)
+ return False
+ return True
+
+ def get_default(self, key):
+ """returns the default value for the key"""
+ data_label = self.data_labels.get(key)
+ if data_label is None:
+ return None
+ return data_label.default
+
+ def save(self, filename):
+ if cmd_opts.freeze:
+ log.warning(f'Settings saving is disabled: {filename}')
+ return
+ with open(filename, "w", encoding="utf8") as file:
+ json.dump(self.data, file, indent=4)
+
+ def same_type(self, x, y):
+ if x is None or y is None:
+ return True
+ type_x = self.typemap.get(type(x), type(x))
+ type_y = self.typemap.get(type(y), type(y))
+ return type_x == type_y
+
+ def load(self, filename):
+ if not os.path.isfile(filename):
+ log.debug(f'Created default config: {filename}')
+ self.save(filename)
+ return
+ with open(filename, "r", encoding="utf8") as file:
+ self.data = json.load(file)
+ if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
+ self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
+ bad_settings = 0
+ for k, v in self.data.items():
+ info = self.data_labels.get(k, None)
+ if info is not None and not self.same_type(info.default, v):
+ log.error(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})")
+ bad_settings += 1
+ if bad_settings > 0:
+ log.error(f"Error: Bad settings found in {filename}")
+
+ def onchange(self, key, func, call=True):
+ item = self.data_labels.get(key)
+ item.onchange = func
+ if call:
+ func()
+
+ def dumpjson(self):
+ d = {k: self.data.get(k, self.data_labels.get(k).default) for k in self.data_labels.keys()}
+ metadata = {
+ k: {
+ "is_stored": k in self.data,
+ "tab_name": v.section[0]
+ } for k, v in self.data_labels.items()
+ }
+ return json.dumps({"values": d, "metadata": metadata})
+
+ def add_option(self, key, info):
+ self.data_labels[key] = info
+
+ def reorder(self):
+ """reorder settings so that all items related to section always go together"""
+ section_ids = {}
+ settings_items = self.data_labels.items()
+ for k, item in settings_items:
+ if item.section not in section_ids:
+ section_ids[item.section] = len(section_ids)
+ self.data_labels = {k: v for k, v in sorted(settings_items, key=lambda x: section_ids[x[1].section])}
+
+ def cast_value(self, key, value):
+ """casts an arbitrary to the same type as this setting's value with key
+ Example: cast_value("eta_noise_seed_delta", "12") -> returns 12 (an int rather than str)
+ """
+ if value is None:
+ return None
+ default_value = self.data_labels[key].default
+ if default_value is None:
+ default_value = getattr(self, key, None)
+ if default_value is None:
+ return None
+ expected_type = type(default_value)
+ if expected_type == bool and value == "False":
+ value = False
+ elif expected_type == type(value):
+ pass
+ else:
+ value = expected_type(value)
+ return value
+
+
+opts = Options()
+config_filename = cmd_opts.config
+opts.load(config_filename)
+cmd_opts = cmd_args.compatibility_args(opts, cmd_opts)
+if cmd_opts.backend == 'diffusers':
+ log.info('Overriding backend to Diffusers')
+ opts.data['sd_backend'] = 'Diffusers'
+if cmd_opts.backend == 'original':
+ log.info('Overriding backend to Diffusers')
+ opts.data['sd_backend'] = 'Original'
+backend = Backend.DIFFUSERS if opts.sd_backend == 'Diffusers' else Backend.ORIGINAL
+
+prompt_styles = modules.styles.StyleDatabase(opts.styles_dir)
+cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or (cmd_opts.server_name or False)) and not cmd_opts.insecure
+devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
+device = devices.device
+batch_cond_uncond = opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram)
+parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram
+mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts)
+mem_mon.start()
+if device.type == 'privateuseone':
+ import modules.dml # pylint: disable=ungrouped-imports
+
+
+def reload_gradio_theme(theme_name=None):
+ global gradio_theme # pylint: disable=global-statement
+ if not theme_name:
+ theme_name = opts.gradio_theme
+ default_font_params = {}
+ res = 0
+ try:
+ req = urllib.request.Request("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono", method="HEAD")
+ res = urllib.request.urlopen(req, timeout=3.0).status
+ except:
+ res = 0
+ if res != 200:
+ log.info('No internet access detected, using default fonts')
+ default_font_params = {
+ 'font':['Helvetica', 'ui-sans-serif', 'system-ui', 'sans-serif'],
+ 'font_mono':['IBM Plex Mono', 'ui-monospace', 'Consolas', 'monospace']
+ }
+ if theme_name == "black-orange":
+ gradio_theme = gr.themes.Default(**default_font_params)
+ elif theme_name.startswith("gradio/"):
+ if theme_name == "gradio/default":
+ gradio_theme = gr.themes.Default(**default_font_params)
+ if theme_name == "gradio/base":
+ gradio_theme = gr.themes.Base(**default_font_params)
+ if theme_name == "gradio/glass":
+ gradio_theme = gr.themes.Glass(**default_font_params)
+ if theme_name == "gradio/monochrome":
+ gradio_theme = gr.themes.Monochrome(**default_font_params)
+ if theme_name == "gradio/soft":
+ gradio_theme = gr.themes.Soft(**default_font_params)
+ else:
+ try:
+ gradio_theme = gr.themes.ThemeClass.from_hub(theme_name)
+ except:
+ log.error("Theme download error accessing HuggingFace")
+ gradio_theme = gr.themes.Default(**default_font_params)
+ log.info(f'Loading UI theme: name={theme_name} style={opts.theme_style}')
+
+
+class TotalTQDM:
+ def __init__(self):
+ self._tqdm = None
+
+ def reset(self):
+ self._tqdm = tqdm.tqdm(
+ desc="Total",
+ total=state.job_count * state.sampling_steps,
+ position=1,
+ )
+
+ def update(self):
+ if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
+ return
+ if self._tqdm is None:
+ self.reset()
+ self._tqdm.update()
+
+ def updateTotal(self, new_total):
+ if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
+ return
+ if self._tqdm is None:
+ self.reset()
+ self._tqdm.total = new_total
+
+ def clear(self):
+ if self._tqdm is not None:
+ self._tqdm.refresh()
+ self._tqdm.close()
+ self._tqdm = None
+
+total_tqdm = TotalTQDM()
+
+
+def restart_server(restart=True):
+ if demo is None:
+ return
+ log.info('Server shutdown requested')
+ try:
+ demo.server.wants_restart = restart
+ demo.server.should_exit = True
+ demo.server.force_exit = True
+ demo.close(verbose=False)
+ demo.server.close()
+ demo.fns = []
+ except:
+ pass
+ if restart:
+ log.info('Server will restart')
+
+
+def restore_defaults(restart=True):
+ if os.path.exists(cmd_opts.config):
+ log.info('Restoring server defaults')
+ os.remove(cmd_opts.config)
+ if os.path.exists(cmd_opts.ui_config):
+ log.info('Restoring UI defaults')
+ os.remove(cmd_opts.ui_config)
+ restart_server(restart)
+
+
+def listfiles(dirname):
+ filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=str.lower) if not x.startswith(".")]
+ return [file for file in filenames if os.path.isfile(file)]
+
+
+def walk_files(path, allowed_extensions=None):
+ if not os.path.exists(path):
+ return
+ if allowed_extensions is not None:
+ allowed_extensions = set(allowed_extensions)
+ for root, _dirs, files in os.walk(path, followlinks=True):
+ for filename in files:
+ if allowed_extensions is not None:
+ _, ext = os.path.splitext(filename)
+ if ext not in allowed_extensions:
+ continue
+ yield os.path.join(root, filename)
+
+
+def html_path(filename):
+ return os.path.join(paths.script_path, "html", filename)
+
+
+def html(filename):
+ path = html_path(filename)
+ if os.path.exists(path):
+ with open(path, encoding="utf8") as file:
+ return file.read()
+ return ""
+
+
+def get_version():
+ version = None
+ if version is None:
+ try:
+ import subprocess
+ res = subprocess.run('git log --pretty=format:"%h %ad" -1 --date=short', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
+ ver = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ' '
+ githash, updated = ver.split(' ')
+ res = subprocess.run('git remote get-url origin', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
+ origin = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ''
+ res = subprocess.run('git branch --show-current', stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True, check=True)
+ branch = res.stdout.decode(encoding = 'utf8', errors='ignore') if len(res.stdout) > 0 else ''
+ version = {
+ 'app': 'sd.next',
+ 'updated': updated,
+ 'hash': githash,
+ 'url': origin.replace('\n', '') + '/tree/' + branch.replace('\n', '')
+ }
+ except:
+ version = { 'app': 'sd.next' }
+ return version
+
+
+class Shared(sys.modules[__name__].__class__):
+ # this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than at program startup.
+ sd_model_val = None
+
+ @property
+ def sd_model(self):
+ import modules.sd_models # pylint: disable=W0621
+ # return modules.sd_models.model_data.sd_model
+ return modules.sd_models.model_data.get_sd_model()
+
+ @sd_model.setter
+ def sd_model(self, value):
+ import modules.sd_models # pylint: disable=W0621
+ modules.sd_models.model_data.set_sd_model(value)
+
+# sd_model: LatentDiffusion = None # this var is here just for IDE's type checking; it cannot be accessed because the class field above will be accessed instead
+sd_model = None
+sys.modules[__name__].__class__ = Shared