Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
S
Stable Diffusion Webui
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
novelai-storage
Stable Diffusion Webui
Commits
02e69633
Commit
02e69633
authored
Jan 13, 2024
by
Sj-Si
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
continue cleanup and redesign.
parent
03650022
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
572 additions
and
187 deletions
+572
-187
html/extra-networks-tree-button.html
html/extra-networks-tree-button.html
+11
-0
javascript/extraNetworks.js
javascript/extraNetworks.js
+203
-91
modules/ui_extra_networks.py
modules/ui_extra_networks.py
+144
-33
style.css
style.css
+214
-63
No files found.
html/extra-networks-tree-button.html
0 → 100644
View file @
02e69633
<span
data-filterable-item-text
hidden
>
{search_terms}
</span>
<button
class=
"action-list-content action-list-content-file"
type=
"button"
onclick=
"extraNetworksTreeOnClick(event, '{tabname}', '{tab_id}');"
>
<span
class=
"action-list-item-visual action-list-item-visual--leading"
>
🗎
</span>
<span
class=
"action-list-item-label action-list-item-label--truncate"
>
{name}
</span>
<span
class=
"action-list-item-action action-list-item-action--trailing"
>
<div
class=
"button-row"
>
{copy_path_button}{metadata_button}{edit_button}
</div>
</span>
</button>
\ No newline at end of file
javascript/extraNetworks.js
View file @
02e69633
...
@@ -16,88 +16,110 @@ function toggleCss(key, css, enable) {
...
@@ -16,88 +16,110 @@ function toggleCss(key, css, enable) {
}
}
function
setupExtraNetworksForTab
(
tabname
)
{
function
setupExtraNetworksForTab
(
tabname
)
{
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_extra_tabs
'
).
classList
.
add
(
'
extra-networks
'
);
var
this_tab
=
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_extra_tabs
'
);
this_tab
.
classList
.
add
(
'
extra-networks
'
);
var
tabs
=
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_extra_tabs > div
'
);
function
registerPrompt
(
tabname
,
id
)
{
var
searchDiv
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_search
'
);
var
textarea
=
gradioApp
().
querySelector
(
"
#
"
+
id
+
"
> label > textarea
"
);
var
search
=
searchDiv
.
querySelector
(
'
textarea
'
);
var
sort
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_sort
'
);
var
sortOrder
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_sortorder
'
);
var
refresh
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_refresh
'
);
var
promptContainer
=
gradioApp
().
querySelector
(
'
.prompt-container-compact#
'
+
tabname
+
'
_prompt_container
'
);
var
negativePrompt
=
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_neg_prompt
'
);
tabs
.
appendChild
(
searchDiv
);
tabs
.
appendChild
(
sort
);
tabs
.
appendChild
(
sortOrder
);
tabs
.
appendChild
(
refresh
);
var
applyFilter
=
function
()
{
var
searchTerm
=
search
.
value
.
toLowerCase
();
gradioApp
().
querySelectorAll
(
'
#
'
+
tabname
+
'
_extra_tabs div.card
'
).
forEach
(
function
(
elem
)
{
var
searchOnly
=
elem
.
querySelector
(
'
.search_only
'
);
var
text
=
Array
.
prototype
.
map
.
call
(
elem
.
querySelectorAll
(
'
.search_terms
'
),
function
(
t
)
{
return
t
.
textContent
.
toLowerCase
()
}).
join
(
"
"
);
var
visible
=
text
.
indexOf
(
searchTerm
)
!=
-
1
;
if
(
searchOnly
&&
searchTerm
.
length
<
4
)
{
visible
=
false
;
}
elem
.
style
.
display
=
visible
?
""
:
"
none
"
;
});
applySort
();
};
var
applySort
=
function
()
{
var
cards
=
gradioApp
().
querySelectorAll
(
'
#
'
+
tabname
+
'
_extra_tabs div.card
'
);
var
reverse
=
sortOrder
.
classList
.
contains
(
"
sortReverse
"
);
var
sortKey
=
sort
.
querySelector
(
"
input
"
).
value
.
toLowerCase
().
replace
(
"
sort
"
,
""
).
replaceAll
(
"
"
,
"
_
"
).
replace
(
/_+$/
,
""
).
trim
()
||
"
name
"
;
sortKey
=
"
sort
"
+
sortKey
.
charAt
(
0
).
toUpperCase
()
+
sortKey
.
slice
(
1
);
var
sortKeyStore
=
sortKey
+
"
-
"
+
(
reverse
?
"
Descending
"
:
"
Ascending
"
)
+
"
-
"
+
cards
.
length
;
if
(
sortKeyStore
==
sort
.
dataset
.
sortkey
)
{
if
(
!
activePromptTextarea
[
tabname
]
)
{
return
;
activePromptTextarea
[
tabname
]
=
textarea
;
}
}
sort
.
dataset
.
sortkey
=
sortKeyStore
;
cards
.
forEach
(
function
(
card
)
{
textarea
.
addEventListener
(
"
focus
"
,
function
(
)
{
card
.
originalParentElement
=
card
.
parentElement
;
activePromptTextarea
[
tabname
]
=
textarea
;
});
});
var
sortedCards
=
Array
.
from
(
cards
);
}
sortedCards
.
sort
(
function
(
cardA
,
cardB
)
{
var
a
=
cardA
.
dataset
[
sortKey
];
this_tab
.
querySelectorAll
(
"
:scope > [id^='
"
+
tabname
+
"
_']
"
).
forEach
(
function
(
elem
)
{
var
b
=
cardB
.
dataset
[
sortKey
];
var
tab_id
=
elem
.
getAttribute
(
"
id
"
);
if
(
!
isNaN
(
a
)
&&
!
isNaN
(
b
))
{
return
parseInt
(
a
)
-
parseInt
(
b
);
var
tabs
=
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_extra_tabs > div
'
);
var
searchDiv
=
gradioApp
().
QuerySelector
(
"
#
"
+
tab_id
+
"
_extra_search
"
);
console
.
log
(
"
HERE:
"
,
tab_id
+
"
_extra_search
"
,
searchDiv
);
var
search
=
searchDiv
.
value
;
var
sort
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_sort
'
);
var
sortOrder
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_sortorder
'
);
var
refresh
=
gradioApp
().
getElementById
(
tabname
+
'
_extra_refresh
'
);
var
promptContainer
=
gradioApp
().
querySelector
(
'
.prompt-container-compact#
'
+
tabname
+
'
_prompt_container
'
);
var
negativePrompt
=
gradioApp
().
querySelector
(
'
#
'
+
tabname
+
'
_neg_prompt
'
);
tabs
.
appendChild
(
searchDiv
);
tabs
.
appendChild
(
sort
);
tabs
.
appendChild
(
sortOrder
);
tabs
.
appendChild
(
refresh
);
var
applyFilter
=
function
()
{
var
searchTerm
=
search
.
value
.
toLowerCase
();
gradioApp
().
querySelectorAll
(
'
#
'
+
tabname
+
'
_extra_tabs div.card
'
).
forEach
(
function
(
elem
)
{
var
searchOnly
=
elem
.
querySelector
(
'
.search_only
'
);
var
text
=
Array
.
prototype
.
map
.
call
(
elem
.
querySelectorAll
(
'
.search_terms
'
),
function
(
t
)
{
return
t
.
textContent
.
toLowerCase
()
}).
join
(
"
"
);
var
visible
=
text
.
indexOf
(
searchTerm
)
!=
-
1
;
if
(
searchOnly
&&
searchTerm
.
length
<
4
)
{
visible
=
false
;
}
elem
.
style
.
display
=
visible
?
""
:
"
none
"
;
});
applySort
();
};
var
applySort
=
function
()
{
var
cards
=
gradioApp
().
querySelectorAll
(
'
#
'
+
tabname
+
'
_extra_tabs div.card
'
);
var
reverse
=
sortOrder
.
classList
.
contains
(
"
sortReverse
"
);
var
sortKey
=
sort
.
querySelector
(
"
input
"
).
value
.
toLowerCase
().
replace
(
"
sort
"
,
""
).
replaceAll
(
"
"
,
"
_
"
).
replace
(
/_+$/
,
""
).
trim
()
||
"
name
"
;
sortKey
=
"
sort
"
+
sortKey
.
charAt
(
0
).
toUpperCase
()
+
sortKey
.
slice
(
1
);
var
sortKeyStore
=
sortKey
+
"
-
"
+
(
reverse
?
"
Descending
"
:
"
Ascending
"
)
+
"
-
"
+
cards
.
length
;
if
(
sortKeyStore
==
sort
.
dataset
.
sortkey
)
{
return
;
}
}
sort
.
dataset
.
sortkey
=
sortKeyStore
;
return
(
a
<
b
?
-
1
:
(
a
>
b
?
1
:
0
));
});
cards
.
forEach
(
function
(
card
)
{
if
(
reverse
)
{
card
.
originalParentElement
=
card
.
parentElement
;
sortedCards
.
reverse
();
});
}
var
sortedCards
=
Array
.
from
(
cards
);
cards
.
forEach
(
function
(
card
)
{
sortedCards
.
sort
(
function
(
cardA
,
cardB
)
{
card
.
remove
();
var
a
=
cardA
.
dataset
[
sortKey
];
});
var
b
=
cardB
.
dataset
[
sortKey
];
sortedCards
.
forEach
(
function
(
card
)
{
if
(
!
isNaN
(
a
)
&&
!
isNaN
(
b
))
{
card
.
originalParentElement
.
appendChild
(
card
);
return
parseInt
(
a
)
-
parseInt
(
b
);
}
return
(
a
<
b
?
-
1
:
(
a
>
b
?
1
:
0
));
});
if
(
reverse
)
{
sortedCards
.
reverse
();
}
cards
.
forEach
(
function
(
card
)
{
card
.
remove
();
});
sortedCards
.
forEach
(
function
(
card
)
{
card
.
originalParentElement
.
appendChild
(
card
);
});
};
search
.
addEventListener
(
"
input
"
,
applyFilter
);
sortOrder
.
addEventListener
(
"
click
"
,
function
()
{
sortOrder
.
classList
.
toggle
(
"
sortReverse
"
);
applySort
();
});
});
};
applyFilter
();
extraNetworksApplySort
[
tab_id
]
=
applySort
;
extraNetworksApplyFilter
[
tab_id
]
=
applyFilter
;
search
.
addEventListener
(
"
input
"
,
applyFilter
);
registerPrompt
(
tab_id
,
tab_id
+
"
_prompt
"
);
sortOrder
.
addEventListener
(
"
click
"
,
function
()
{
registerPrompt
(
tab_id
,
tab_id
+
"
_neg_prompt
"
);
sortOrder
.
classList
.
toggle
(
"
sortReverse
"
);
applySort
();
});
});
applyFilter
();
extraNetworksApplySort
[
tabname
]
=
applySort
;
extraNetworksApplyFilter
[
tabname
]
=
applyFilter
;
}
}
function
extraNetworksMovePromptToTab
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
)
{
function
extraNetworksMovePromptToTab
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
)
{
...
@@ -136,12 +158,12 @@ function clearSearch(tabname) {
...
@@ -136,12 +158,12 @@ function clearSearch(tabname) {
function
extraNetworksUnrelatedTabSelected
(
tabname
)
{
// called from python when user selects an unrelated tab (generate)
function
extraNetworksUnrelatedTabSelected
(
tabname
)
{
// called from python when user selects an unrelated tab (generate)
extraNetworksMovePromptToTab
(
tabname
,
''
,
false
,
false
);
extraNetworksMovePromptToTab
(
tabname
,
''
,
false
,
false
);
clearSearch
(
tabname
);
//
clearSearch(tabname);
}
}
function
extraNetworksTabSelected
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
)
{
// called from python when user selects an extra networks tab
function
extraNetworksTabSelected
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
)
{
// called from python when user selects an extra networks tab
extraNetworksMovePromptToTab
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
);
extraNetworksMovePromptToTab
(
tabname
,
id
,
showPrompt
,
showNegativePrompt
);
clearSearch
(
tabname
);
//
clearSearch(tabname);
}
}
function
applyExtraNetworkFilter
(
tabname
)
{
function
applyExtraNetworkFilter
(
tabname
)
{
...
@@ -159,23 +181,6 @@ var activePromptTextarea = {};
...
@@ -159,23 +181,6 @@ var activePromptTextarea = {};
function
setupExtraNetworks
()
{
function
setupExtraNetworks
()
{
setupExtraNetworksForTab
(
'
txt2img
'
);
setupExtraNetworksForTab
(
'
txt2img
'
);
setupExtraNetworksForTab
(
'
img2img
'
);
setupExtraNetworksForTab
(
'
img2img
'
);
function
registerPrompt
(
tabname
,
id
)
{
var
textarea
=
gradioApp
().
querySelector
(
"
#
"
+
id
+
"
> label > textarea
"
);
if
(
!
activePromptTextarea
[
tabname
])
{
activePromptTextarea
[
tabname
]
=
textarea
;
}
textarea
.
addEventListener
(
"
focus
"
,
function
()
{
activePromptTextarea
[
tabname
]
=
textarea
;
});
}
registerPrompt
(
'
txt2img
'
,
'
txt2img_prompt
'
);
registerPrompt
(
'
txt2img
'
,
'
txt2img_neg_prompt
'
);
registerPrompt
(
'
img2img
'
,
'
img2img_prompt
'
);
registerPrompt
(
'
img2img
'
,
'
img2img_neg_prompt
'
);
}
}
onUiLoaded
(
setupExtraNetworks
);
onUiLoaded
(
setupExtraNetworks
);
...
@@ -262,6 +267,106 @@ function saveCardPreview(event, tabname, filename) {
...
@@ -262,6 +267,106 @@ function saveCardPreview(event, tabname, filename) {
event
.
preventDefault
();
event
.
preventDefault
();
}
}
function
extraNetworksTreeProcessFileClick
(
event
,
btn
,
tabname
,
tab_id
)
{
/**
* Processes `onclick` events when user clicks on files in tree.
*
* @param event The generated event.
* @param btn The clicked `action-list-item` button.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param tab_id The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var
par
=
btn
.
parentElement
;
var
search_id
=
tabname
+
"
_
"
+
tab_id
+
"
_extra_search
"
;
var
type
=
par
.
getAttribute
(
"
data-tree-entry-type
"
);
var
path
=
par
.
getAttribute
(
"
data-path
"
);
}
function
extraNetworksTreeProcessDirectoryClick
(
event
,
btn
)
{
/**
* Processes `onclick` events when user clicks on directories in tree.
*
* Here is how the tree reacts to clicks for various states:
* unselected unopened directory: Diretory is selected and expanded.
* unselected opened directory: Directory is selected.
* selected opened directory: Directory is collapsed and deselected.
* chevron is clicked: Directory is expanded or collapsed. Selected state unchanged.
*
* @param event The generated event.
* @param btn The clicked `action-list-item` button.
*/
var
ul
=
btn
.
nextElementSibling
;
// This is the actual target that the user clicked on within the target button.
// We use this to detect if the chevron was clicked.
var
true_targ
=
event
.
target
;
function
_expand_or_collapse
(
_ul
,
_btn
)
{
// Expands <ul> if it is collapsed, collapses otherwise. Updates button attributes.
if
(
_ul
.
hasAttribute
(
"
data-hidden
"
))
{
_ul
.
removeAttribute
(
"
data-hidden
"
);
_btn
.
setAttribute
(
"
expanded
"
,
"
true
"
);
}
else
{
_ul
.
setAttribute
(
"
data-hidden
"
,
""
);
_btn
.
setAttribute
(
"
expanded
"
,
"
false
"
);
}
}
function
_remove_selected_from_all
()
{
// Removes the `selected` attribute from all buttons.
var
sels
=
document
.
querySelectorAll
(
"
button.action-list-content
"
);
[...
sels
].
forEach
(
el
=>
{
el
.
removeAttribute
(
"
selected
"
);
})
}
function
_select_button
(
_btn
)
{
// Removes `selected` attribute from all buttons then adds to passed button.
_remove_selected_from_all
();
_btn
.
setAttribute
(
"
selected
"
,
""
);
}
// If user clicks on the chevron, then we do not select the folder.
if
(
true_targ
.
matches
(
"
.action-list-item-action--leading, .action-list-item-action-chevron
"
))
{
_expand_or_collapse
(
ul
,
btn
);
}
else
{
// User clicked anywhere else on the button.
if
(
btn
.
hasAttribute
(
"
selected
"
)
&&
!
ul
.
hasAttribute
(
"
data-hidden
"
))
{
// If folder is select and open, collapse and deselect button.
_expand_or_collapse
(
ul
,
btn
);
btn
.
removeAttribute
(
"
selected
"
);
}
else
if
(
!
(
!
btn
.
hasAttribute
(
"
selected
"
)
&&
!
ul
.
hasAttribute
(
"
data-hidden
"
)))
{
// If folder is open and not selected, then we don't collapse; just select.
// NOTE: Double inversion sucks but it is the clearest way to show the branching here.
_expand_or_collapse
(
ul
,
btn
);
_select_button
(
btn
);
}
else
{
// All other cases, just select the button.
_select_button
(
btn
);
}
}
}
function
extraNetworksTreeOnClick
(
event
,
tabname
,
tab_id
)
{
/**
* Handles `onclick` events for buttons within an `extra-network-tree .action-list--tree`.
*
* Determines whether the clicked button in the tree is for a file entry or a directory
* then calls the appropriate function.
*
* @param event The generated event.
* @param tabname The name of the active tab in the sd webui. Ex: txt2img, img2img, etc.
* @param tab_id The id of the active extraNetworks tab. Ex: lora, checkpoints, etc.
*/
var
btn
=
event
.
currentTarget
;
var
par
=
btn
.
parentElement
;
if
(
par
.
getAttribute
(
"
data-tree-entry-type
"
)
===
"
file
"
)
{
extraNetworksTreeProcessFileClick
(
event
,
btn
,
tabname
,
tab_id
);
}
else
{
extraNetworksTreeProcessDirectoryClick
(
event
,
btn
);
}
}
function
extraNetworksFolderClick
(
event
,
tabs_id
)
{
function
extraNetworksFolderClick
(
event
,
tabs_id
)
{
// If folder is open but not selected, we don't want to collapse it. Instead
// If folder is open but not selected, we don't want to collapse it. Instead
// we override the removal of the "open" attribute so that the folder is
// we override the removal of the "open" attribute so that the folder is
...
@@ -434,3 +539,10 @@ window.addEventListener("keydown", function(event) {
...
@@ -434,3 +539,10 @@ window.addEventListener("keydown", function(event) {
closePopup
();
closePopup
();
}
}
});
});
function
testprint
(
e
)
{
console
.
log
(
e
);
}
const
testinput
=
gradioApp
().
querySelector
(
"
#txt2img_lora_extra_search
"
);
testinput
.
addEventListener
(
"
input
"
,
testprint
);
\ No newline at end of file
modules/ui_extra_networks.py
View file @
02e69633
...
@@ -19,6 +19,90 @@ extra_pages = []
...
@@ -19,6 +19,90 @@ extra_pages = []
allowed_dirs
=
set
()
allowed_dirs
=
set
()
default_allowed_preview_extensions
=
[
"png"
,
"jpg"
,
"jpeg"
,
"webp"
,
"gif"
]
default_allowed_preview_extensions
=
[
"png"
,
"jpg"
,
"jpeg"
,
"webp"
,
"gif"
]
tree_tpl
=
(
"<div class='action-list-search'>"
"<input "
"id='{tabname}_{tab_id}_extra_search' "
"class='action-list-search-text' "
"type='search' "
"placeholder='Filter files'"
">"
"</div>"
"<ul class='action-list action-list--tree'>"
"{content}"
"</ul>"
)
tree_ul_tpl
=
(
"<ul class='action-list action-list--subgroup' data-hidden>"
"{content}"
"</ul>"
)
tree_li_dir_tpl
=
(
"<li "
"class='action-list-item action-list-item--has-subitem' "
"data-path='{path}' "
"data-tree-entry-type='dir'>"
"{content}"
"</li>"
)
tree_li_file_tpl
=
(
"<li "
"id='file-tree-item-{hash}' "
"class='action-list-item action-list-item--subitem' "
"data-path='{path}' "
"data-tree-entry-type='file'>"
"{content}"
"</li>"
)
tree_btn_dir_tpl
=
(
"<button "
"class='action-list-content action-list-content-dir' "
"type='button' "
"expanded='false' "
"onclick='extraNetworksTreeOnClick(event,
\"
{tabname}
\"
,
\"
{tab_id}
\"
)'>"
"<span class='action-list-item-action action-list-item-action--leading'>"
"<i class='action-list-item-action-chevron'></i>"
"</span>"
"<span class='action-list-item-visual action-list-item-visual--leading'>🗀</span>"
"<span class='action-list-item-label action-list-item-label--truncate'>{label}</span>"
"</button>"
)
tree_btn_file_action_buttons_tpl
=
(
"<div "
"class='copy-path-button card-button' "
"title='Copy path to clipboard' "
"onclick='extraNetworksCopyCardPath(event, {path})' "
"data-clipboard-text={path}>"
"</div>"
"<div "
"class='metadata-button card-button' "
"title='Show internal metadata' "
"onclick='extraNetworksRequestMetadata(event, {tab_id}, {filename})'>"
"</div>"
"<div "
"class='edit-button card-button' "
"title='Edit metadata' "
"onclick='extraNetworksEditUserMetadata(event, {tabname}, {tab_id}, {filename})'>"
"</div>"
)
tree_btn_file_tpl
=
(
"<span data-filterable-item-text hidden>{filter}</span>"
"<button "
"class='action-list-content action-list-content-file' "
"type='button' "
"onclick='extraNetworksTreeOnClick(event,
\"
{tabname}
\"
,
\"
{tab_id}
\"
)'>"
"<span class='action-list-item-visual action-list-item-visual--leading'>🗎</span>"
"<span class='action-list-item-label action-list-item-label--truncate'>{label}</span>"
"<span class='action-list-item-action action-list-item-action--trailing'>{buttons}</span>"
"</button>"
)
@
functools
.
cache
@
functools
.
cache
def
allowed_preview_extensions_with_extra
(
extra_extensions
=
None
):
def
allowed_preview_extensions_with_extra
(
extra_extensions
=
None
):
return
set
(
default_allowed_preview_extensions
)
|
set
(
extra_extensions
or
[])
return
set
(
default_allowed_preview_extensions
)
|
set
(
extra_extensions
or
[])
...
@@ -160,6 +244,7 @@ class ExtraNetworksPage:
...
@@ -160,6 +244,7 @@ class ExtraNetworksPage:
self
.
extra_networks_pane_template
=
shared
.
html
(
"extra-networks-pane.html"
)
self
.
extra_networks_pane_template
=
shared
.
html
(
"extra-networks-pane.html"
)
self
.
card_page_template
=
shared
.
html
(
"extra-networks-card.html"
)
self
.
card_page_template
=
shared
.
html
(
"extra-networks-card.html"
)
self
.
card_page_minimal_template
=
shared
.
html
(
"extra-networks-card-minimal.html"
)
self
.
card_page_minimal_template
=
shared
.
html
(
"extra-networks-card-minimal.html"
)
self
.
tree_button_template
=
shared
.
html
(
"extra-networks-tree-button.html"
)
self
.
allow_prompt
=
True
self
.
allow_prompt
=
True
self
.
allow_negative_prompt
=
False
self
.
allow_negative_prompt
=
False
self
.
metadata
=
{}
self
.
metadata
=
{}
...
@@ -279,7 +364,9 @@ class ExtraNetworksPage:
...
@@ -279,7 +364,9 @@ class ExtraNetworksPage:
"search_terms"
:
search_terms_html
,
"search_terms"
:
search_terms_html
,
"sort_keys"
:
sort_keys
,
"sort_keys"
:
sort_keys
,
"style"
:
f
"'display: none; {height}{width}; font-size: {shared.opts.extra_networks_card_text_scale*100}
%
'"
,
"style"
:
f
"'display: none; {height}{width}; font-size: {shared.opts.extra_networks_card_text_scale*100}
%
'"
,
"tabname"
:
quote_js
(
tabname
),
"tabname"
:
tabname
,
"tab_id"
:
self
.
id_page
,
}
}
if
template
:
if
template
:
...
@@ -306,55 +393,81 @@ class ExtraNetworksPage:
...
@@ -306,55 +393,81 @@ class ExtraNetworksPage:
if
not
tree
:
if
not
tree
:
return
res
return
res
file_template
=
"<li class='file-item'>{card}</li>"
dir_template
=
(
"<details {attributes} class='folder-item'>"
"<summary class='folder-item-summary' data-path='{data_path}' "
"onclick='extraNetworksFolderClick(event,
\"
{tabname}_extra_search
\"
);'>"
"{folder_name}"
"</summary>"
"<ul class='folder-container'>{content}</ul>"
"</details>"
)
def
_build_tree
(
data
:
Optional
[
dict
[
str
,
ExtraNetworksItem
]]
=
None
)
->
str
:
def
_build_tree
(
data
:
Optional
[
dict
[
str
,
ExtraNetworksItem
]]
=
None
)
->
str
:
"""Recursively builds HTML for a tree."""
"""Recursively builds HTML for a tree."""
_res
=
""
_res
=
""
if
not
data
:
if
not
data
:
return
"<li style='list-style-type:
\"
⚠️
\"
;'>DIRECTORY IS EMPTY</li>"
return
(
"<details open disabled class='folder-item-empty'>"
"<summary class='folder-item-summary-empty'>Directory is empty</summary>"
"</details>"
)
for
k
,
v
in
sorted
(
data
.
items
(),
key
=
lambda
x
:
shared
.
natural_sort_key
(
x
[
0
])):
for
k
,
v
in
sorted
(
data
.
items
(),
key
=
lambda
x
:
shared
.
natural_sort_key
(
x
[
0
])):
if
isinstance
(
v
,
(
ExtraNetworksItem
,)):
if
isinstance
(
v
,
(
ExtraNetworksItem
,)):
item_html
=
self
.
create_item_html
(
tabname
,
v
.
item
,
self
.
card_page_minimal_template
)
_action_buttons
=
tree_btn_file_action_buttons_tpl
.
format
(
_res
+=
file_template
.
format
(
**
{
"card"
:
item_html
})
**
{
"path"
:
quote_js
(
k
),
"filename"
:
quote_js
(
v
.
item
[
"name"
]),
"tabname"
:
quote_js
(
tabname
),
"tab_id"
:
quote_js
(
self
.
id_page
),
}
)
_btn
=
tree_btn_file_tpl
.
format
(
**
{
"label"
:
v
.
item
[
"name"
],
"filter"
:
v
.
item
[
"search_terms"
],
"tabname"
:
tabname
,
"tab_id"
:
self
.
id_page
,
"buttons"
:
_action_buttons
,
}
)
_li
=
tree_li_file_tpl
.
format
(
**
{
"hash"
:
v
.
item
[
"shorthash"
],
"path"
:
k
,
"type"
:
"file"
,
#"content": _btn,
"content"
:
self
.
create_item_html
(
tabname
,
v
.
item
,
self
.
tree_button_template
),
}
)
_res
+=
_li
#item_html = self.create_item_html(tabname, v.item, self.card_page_minimal_template)
#_res += file_template.format(**{"card": item_html})
else
:
else
:
_
res
+=
dir_template
.
format
(
_
btn
=
tree_btn_dir_tpl
.
format
(
**
{
**
{
"
attributes"
:
""
,
"
label"
:
os
.
path
.
basename
(
k
)
,
"tabname"
:
tabname
,
"tabname"
:
tabname
,
"folder_name"
:
os
.
path
.
basename
(
k
),
"tab_id"
:
self
.
id_page
,
"data_path"
:
k
,
"content"
:
_build_tree
(
v
),
}
}
)
)
_ul
=
tree_ul_tpl
.
format
(
**
{
"content"
:
_build_tree
(
v
)})
_li
=
tree_li_dir_tpl
.
format
(
**
{
"content"
:
_btn
+
_ul
,
"path"
:
k
})
_res
+=
_li
return
_res
return
_res
# Add each root directory to the tree.
# Add each root directory to the tree.
for
k
,
v
in
sorted
(
tree
.
items
(),
key
=
lambda
x
:
shared
.
natural_sort_key
(
x
[
0
])):
for
k
,
v
in
sorted
(
tree
.
items
(),
key
=
lambda
x
:
shared
.
natural_sort_key
(
x
[
0
])):
# If root is empty, append the "disabled" attribute to the template details tag.
# If root is empty, append the "disabled" attribute to the template details tag.
res
+=
"<ul class='folder-container'>"
btn
=
tree_btn_dir_tpl
.
format
(
res
+=
dir_template
.
format
(
**
{
**
{
"
attributes"
:
"open"
if
v
else
"open"
,
"
label"
:
os
.
path
.
basename
(
k
)
,
"tabname"
:
tabname
,
"tabname"
:
tabname
,
"folder_name"
:
os
.
path
.
basename
(
k
),
"tab_id"
:
self
.
id_page
,
"data_path"
:
k
,
"content"
:
_build_tree
(
v
),
}
}
)
)
res
+=
"</ul>"
ul
=
tree_ul_tpl
.
format
(
**
{
"content"
:
_build_tree
(
v
)})
res
+=
"</ul>"
li
=
tree_li_dir_tpl
.
format
(
**
{
"content"
:
btn
+
ul
,
"path"
:
k
})
return
res
res
+=
li
return
tree_tpl
.
format
(
**
{
"content"
:
res
,
"tabname"
:
tabname
,
"tab_id"
:
self
.
id_page
,
}
)
def
create_card_view_html
(
self
,
tabname
):
def
create_card_view_html
(
self
,
tabname
):
res
=
""
res
=
""
...
@@ -375,7 +488,7 @@ class ExtraNetworksPage:
...
@@ -375,7 +488,7 @@ class ExtraNetworksPage:
tree_view_html
=
self
.
create_tree_view_html
(
tabname
)
tree_view_html
=
self
.
create_tree_view_html
(
tabname
)
card_view_html
=
self
.
create_card_view_html
(
tabname
)
card_view_html
=
self
.
create_card_view_html
(
tabname
)
network_type_id
=
self
.
name
.
replace
(
" "
,
"_"
)
network_type_id
=
self
.
id_page
return
self
.
extra_networks_pane_template
.
format
(
return
self
.
extra_networks_pane_template
.
format
(
**
{
**
{
...
@@ -506,7 +619,7 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
...
@@ -506,7 +619,7 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
ui
.
pages
.
append
(
page_elem
)
ui
.
pages
.
append
(
page_elem
)
page_elem
.
change
(
page_elem
.
change
(
fn
=
lambda
:
None
,
fn
=
lambda
:
None
,
_js
=
f
"function(){{applyExtraNetworkFilter({tabname}_extra_search); return []}}"
,
_js
=
f
"function(){{applyExtraNetworkFilter({tabname}_
{page.id_page}_
extra_search); return []}}"
,
inputs
=
[],
inputs
=
[],
outputs
=
[],
outputs
=
[],
)
)
...
@@ -517,13 +630,11 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
...
@@ -517,13 +630,11 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname):
related_tabs
.
append
(
tab
)
related_tabs
.
append
(
tab
)
edit_search
=
gr
.
Textbox
(
''
,
show_label
=
False
,
elem_id
=
f
"{tabname}_extra_search"
,
elem_classes
=
"search"
,
placeholder
=
"Search..."
,
visible
=
False
,
interactive
=
True
)
dropdown_sort
=
gr
.
Dropdown
(
choices
=
[
'Path'
,
'Name'
,
'Date Created'
,
'Date Modified'
,
],
value
=
shared
.
opts
.
extra_networks_card_order_field
,
elem_id
=
tabname
+
"_extra_sort"
,
elem_classes
=
"sort"
,
multiselect
=
False
,
visible
=
False
,
show_label
=
False
,
interactive
=
True
,
label
=
tabname
+
"_extra_sort_order"
)
dropdown_sort
=
gr
.
Dropdown
(
choices
=
[
'Path'
,
'Name'
,
'Date Created'
,
'Date Modified'
,
],
value
=
shared
.
opts
.
extra_networks_card_order_field
,
elem_id
=
tabname
+
"_extra_sort"
,
elem_classes
=
"sort"
,
multiselect
=
False
,
visible
=
False
,
show_label
=
False
,
interactive
=
True
,
label
=
tabname
+
"_extra_sort_order"
)
button_sortorder
=
ToolButton
(
switch_values_symbol
,
elem_id
=
tabname
+
"_extra_sortorder"
,
elem_classes
=
[
"sortorder"
]
+
([]
if
shared
.
opts
.
extra_networks_card_order
==
"Ascending"
else
[
"sortReverse"
]),
visible
=
False
,
tooltip
=
"Invert sort order"
)
button_sortorder
=
ToolButton
(
switch_values_symbol
,
elem_id
=
tabname
+
"_extra_sortorder"
,
elem_classes
=
[
"sortorder"
]
+
([]
if
shared
.
opts
.
extra_networks_card_order
==
"Ascending"
else
[
"sortReverse"
]),
visible
=
False
,
tooltip
=
"Invert sort order"
)
button_refresh
=
gr
.
Button
(
'Refresh'
,
elem_id
=
tabname
+
"_extra_refresh"
,
visible
=
False
)
button_refresh
=
gr
.
Button
(
'Refresh'
,
elem_id
=
tabname
+
"_extra_refresh"
,
visible
=
False
)
tab_controls
=
[
tab_controls
=
[
edit_search
,
dropdown_sort
,
dropdown_sort
,
button_sortorder
,
button_sortorder
,
button_refresh
,
button_refresh
,
...
...
style.css
View file @
02e69633
...
@@ -955,15 +955,15 @@ footer {
...
@@ -955,15 +955,15 @@ footer {
color
:
white
;
color
:
white
;
}
}
.extra-network-pane
.copy-path-button
:before
{
.extra-network-pane
.copy-path-button
:
:
before
{
content
:
"⎘"
;
content
:
"⎘"
;
}
}
.extra-network-pane
.metadata-button
:before
{
.extra-network-pane
.metadata-button
:
:
before
{
content
:
"🛈"
;
content
:
"🛈"
;
}
}
.extra-network-pane
.edit-button
:before
{
.extra-network-pane
.edit-button
:
:
before
{
content
:
"🛠"
;
content
:
"🛠"
;
}
}
...
@@ -1188,102 +1188,253 @@ body.resizing .resize-handle {
...
@@ -1188,102 +1188,253 @@ body.resizing .resize-handle {
border-left
:
1px
dashed
var
(
--border-color-primary
);
border-left
:
1px
dashed
var
(
--border-color-primary
);
}
}
.extra-network-pane
.card-minimal
{
/* ========================= */
display
:
inline-flex
;
.extra-network-pane
{
flex-grow
:
1
;
display
:
flex
;
position
:
relative
;
overflow
:
hidden
;
cursor
:
pointer
;
font-size
:
1rem
;
font-weight
:
bold
;
line-break
:
anywhere
;
}
}
/* Pushes buttons to right */
.extra-network-pane
.extra-network-cards
{
.extra-network-pane
.card-minimal
.name
{
display
:
block
;
flex-grow
:
1
;
}
}
.folder-container
{
.extra-network-pane
.extra-network-tree
{
margin-left
:
1.5em
!important
;
display
:
block
;
font-size
:
1rem
;
min-width
:
25%
;
border
:
1px
solid
var
(
--block-border-color
);
overflow
:
hidden
;
}
}
.file-item
,
.extra-network-tree
.action-list--tree
{
.folder-item
,
.folder-item-summary
{
padding-left
:
0.05rem
;
cursor
:
pointer
;
cursor
:
pointer
;
-webkit-user-select
:
none
;
-moz-user-select
:
none
;
-ms-user-select
:
none
;
user-select
:
none
;
user-select
:
none
;
font-size
:
1rem
;
margin
:
0
;
padding
:
0
;
}
}
.extra-network-pane
.extra-network-tree
.folder-item-summary
:hover
,
/* Remove auto indentation from tree. Will be overridden later. */
.extra-network-pane
.extra-network-tree
.file-item
:hover
{
.extra-network-tree
.action-list--subgroup
{
-webkit-transition
:
all
0.1s
ease-in-out
;
margin
:
0
!important
;
transition
:
all
0.1s
ease-in-out
;
padding
:
0
!important
;
background-color
:
var
(
--neutral-200
);
box-shadow
:
0.6rem
0
0
var
(
--body-background-fill
)
inset
,
0.8rem
0
0
var
(
--neutral-800
)
inset
;
}
/* Set indentation for each depth of tree. */
.extra-network-tree
.action-list--subgroup
>
.action-list-item
{
margin-left
:
0.4rem
!important
;
padding-left
:
0.4rem
!important
;
}
}
.dark
.extra-network-pane
.extra-network-tree
.folder-item-summary
:hover
,
/* Styles for tree <ul> elements. */
.dark
.extra-network-pane
.extra-network-tree
.file-item
:hover
{
.extra-network-tree
.action-list
{
}
/* Styles for tree <li> elements. */
.extra-network-tree
.action-list-item
{
list-style
:
none
;
position
:
relative
;
background-color
:
transparent
;
}
/* Directory <ul> */
.extra-network-tree
.action-list-content
[
expanded
=
false
]+
.action-list--subgroup
{
height
:
0
;
overflow
:
hidden
;
visibility
:
hidden
;
opacity
:
0
;
}
.extra-network-tree
.action-list-content
[
expanded
=
true
]+
.action-list--subgroup
{
height
:
auto
;
overflow
:
visible
;
visibility
:
visible
;
opacity
:
1
;
}
/* File <li> */
.extra-network-tree
.action-list-item--subitem
{
}
/* <li> containing <ul> */
.extra-network-tree
.action-list-item--has-subitem
{
}
/* BUTTON ELEMENTS */
/* <button> */
.extra-network-tree
.action-list-content
{
position
:
relative
;
display
:
grid
;
width
:
100%
;
padding
:
6px
8px
;
font-size
:
1rem
;
text-align
:
left
;
user-select
:
none
;
background-color
:
transparent
;
border
:
none
;
transition
:
background
33.333ms
linear
;
grid-template-rows
:
min-content
;
grid-template-areas
:
"leading-action leading-visual label trailing-visual spacer trailing-action"
;
grid-template-columns
:
min-content
min-content
minmax
(
0
,
auto
)
min-content
1
fr
min-content
;
grid-gap
:
0.1em
;
align-items
:
start
;
flex-grow
:
1
;
flex-basis
:
100%
;
}
.dark
.extra-network-tree
button
.action-list-content
:hover
{
-webkit-transition
:
all
0.05s
ease-in-out
;
-webkit-transition
:
all
0.05s
ease-in-out
;
transition
:
all
0.05s
ease-in-out
;
transition
:
all
0.05s
ease-in-out
;
background-color
:
var
(
--neutral-800
);
background-color
:
var
(
--neutral-800
);
}
}
/* prevents clicking/collapsing of details tags when disabled attribute is used*/
.dark
.extra-network-tree
button
.action-list-content
[
selected
]
{
.extra-network-pane
.extra-network-tree
details
[
disabled
]
summary
{
background-color
:
var
(
--neutral-700
);
pointer-events
:
none
;
user-select
:
none
;
}
}
.extra-network-pane
.extra-network-tree
details
.folder-item
>
summary
{
.extra-network-tree
button
.action-list-content
:hover
{
list-style-type
:
'📁'
;
-webkit-transition
:
all
0.05s
ease-in-out
;
text-overflow
:
ellipsis
;
transition
:
all
0.05s
ease-in-out
;
background-color
:
var
(
--neutral-200
);
}
}
.extra-network-pane
.extra-network-tree
details
.folder-item
[
open
]
>
summary
{
.extra-network-tree
button
.action-list-content
[
selected
]
{
list-style-type
:
'📂'
;
background-color
:
var
(
--neutral-300
);
text-overflow
:
ellipsis
;
}
}
.extra-network-pane
.extra-network-tree
ul
.folder-container
{
/* Buttons for directories. */
list-style
:
none
;
.extra-network-tree
.action-list-content-dir
{
font-size
:
1rem
;
text-overflow
:
ellipsis
;
}
}
.extra-network-pane
.extra-network-tree
li
.file-item
{
/* Buttons for files. */
display
:
flex
;
.extra-network-tree
.action-list-content-file
{
margin-left
:
2em
;
}
/* Text for button. */
.extra-network-tree
.action-list-item-label
{
position
:
relative
;
position
:
relative
;
align-items
:
center
;
line-height
:
1.25em
;
color
:
var
(
--button-secondary-text-color
);
grid-area
:
label
;
padding-left
:
0.5em
;
}
}
.extra-network-pane
.extra-network-tree
li
.file-item
::before
{
/* Text for button truncated. */
content
:
'📄'
;
.extra-network-tree
.action-list-item-label--truncate
{
font-size
:
0.85rem
;
overflow
:
hidden
;
vertical-align
:
middle
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
}
.extra-network-pane
{
/* Icon for button. */
display
:
flex
;
.extra-network-tree
.action-list-item-visual
{
min-height
:
1em
;
color
:
var
(
--button-secondary-text-color
);
pointer-events
:
none
;
align-items
:
right
;
}
}
.extra-network-pane
.extra-network-tree
{
/* Icon for button when it is before label. */
font-size
:
1rem
;
.extra-network-tree
.action-list-item-visual--leading
{
min-width
:
25%
;
grid-area
:
leading-visual
;
max-width
:
25%
;
border
:
1px
solid
var
(
--block-border-color
);
}
}
.extra-network-pane
.extra-network-cards
{
/* Icon for button when it is after label. */
.extra-network-tree
.action-list-item-visual--trailing
{
grid-area
:
trailing-visual
;
}
/* Dropdown arrow for button. */
.extra-network-tree
.action-list-item-action--leading
{
margin-right
:
1em
;
}
/* Define the animation for the arrow when it is clicked. */
.extra-network-tree
.action-list-content-dir
[
expanded
=
false
]
.action-list-item-action
{
-ms-transform
:
rotate
(
0deg
);
-webkit-transform
:
rotate
(
0deg
);
transform
:
rotate
(
0deg
);
transition
:
transform
0.2s
;
}
.extra-network-tree
.action-list-content-dir
[
expanded
=
true
]
.action-list-item-action
{
-ms-transform
:
rotate
(
90deg
);
-webkit-transform
:
rotate
(
90deg
);
transform
:
rotate
(
90deg
);
transition
:
transform
0.2s
;
}
.action-list-item-action-chevron
{
display
:
inline-block
;
padding
:
0.3em
;
box-shadow
:
0.1em
0.1em
0
0px
var
(
--neutral-200
)
inset
;
transform
:
rotate
(
135deg
);
}
.extra-network-tree
.action-list-item-action--leading
{
grid-area
:
leading-action
;
}
/* Dropdown arrow for button when it is after label. UNUSED? */
.extra-network-tree
.action-list-item-action--trailing
{
grid-area
:
trailing-action
;
}
.extra-network-tree
.action-list-item-action
.button-row
{
display
:
flex
;
flex-grow
:
1
;
flex-grow
:
1
;
border
:
1px
solid
var
(
--block-border-color
);
}
}
.dark
.extra-network-tree
.folder-item-summary.selected
{
.extra-network-tree
.action-list-content
.button-row
{
background-color
:
var
(
--neutral-800
);
display
:
inline-flex
;
visibility
:
hidden
!important
;
color
:
white
;
}
}
.extra-network-tree
.
folder-item-summary.selected
{
.extra-network-tree
.
action-list-content
:hover
.button-row
{
background-color
:
var
(
--neutral-200
)
;
visibility
:
visible
;
}
}
/* Add icon to left side of <input> */
.extra-network-tree
.action-list-search
::before
{
content
:
"🔎︎"
;
position
:
absolute
;
margin
:
0.5em
;
font-size
:
1em
;
color
:
var
(
--input-placeholder-color
);
}
.extra-network-tree
.action-list-search
{
position
:
relative
;
margin
:
0.5em
;
}
.extra-network-tree
.action-list-search
.action-list-search-text
{
border
:
1px
solid
var
(
--button-secondary-border-color
);
border-radius
:
0.5em
;
color
:
var
(
--button-secondary-text-color
);
background-color
:
transparent
;
width
:
100%
;
padding-left
:
2em
;
line-height
:
1em
;
}
/* <input> clear button (x on right side) styling */
.extra-network-tree
.action-list-search
.action-list-search-text
::-webkit-search-cancel-button
{
-webkit-appearance
:
none
;
appearance
:
none
;
cursor
:
pointer
;
height
:
1em
;
width
:
1em
;
mask-image
:
url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>')
;
mask-repeat
:
no-repeat
;
mask-position
:
center
center
;
mask-size
:
100%
;
background-color
:
var
(
--input-placeholder-color
);
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment