Compare commits
1562 Commits
Author | SHA1 | Date | |
---|---|---|---|
5b47da67f6 | |||
292f68ff97 | |||
3b554d881a | |||
4bc6e51862 | |||
427861cf13 | |||
da3e7a2eb8 | |||
2979f04c82 | |||
1949d8a50c | |||
ee66c799e0 | |||
7c50b8bf94 | |||
141ff74ece | |||
321e5f1ed6 | |||
6d131d9d8e | |||
7e69b8eb31 | |||
4e0b33e6a4 | |||
54f7e6fcb8 | |||
529169c4da | |||
a2c8c99215 | |||
e8bf3fd009 | |||
465676e9ea | |||
af53b57047 | |||
54b5f75905 | |||
4348333497 | |||
cc31110bcf | |||
f7c04bf7a6 | |||
029509ebad | |||
65102bb64d | |||
b96b55c5ce | |||
1f5aba010e | |||
f0b3bea4e3 | |||
84fae2d9e0 | |||
0b96fa112d | |||
c64bcd23d3 | |||
efd9a22bb5 | |||
159c3edfe3 | |||
f74fa8657b | |||
648b142a4b | |||
426f92595e | |||
82a8d9b644 | |||
ff9430b8a2 | |||
2e69ffcb5e | |||
0ea38db7ef | |||
2706149399 | |||
3d0cdc1cb6 | |||
ac605e9352 | |||
5432297691 | |||
e37be0f954 | |||
a99209b674 | |||
cb02b5ba18 | |||
14714b950d | |||
13654cb8c0 | |||
00276228cf | |||
8583bb8d7b | |||
d48951fe00 | |||
99bdcfa0a5 | |||
e64e1a92e6 | |||
e278e639a3 | |||
c4bad5c454 | |||
da41a74efc | |||
9c91f57b19 | |||
f14afcd129 | |||
5c1a3d82d7 | |||
e02a917569 | |||
347fa0fda1 | |||
6510d4cb02 | |||
91e4ccf6f8 | |||
36249874bc | |||
d2b5d6cce9 | |||
b2922741c9 | |||
300f3e27db | |||
d7330b80a9 | |||
acdd7667b7 | |||
8114fa3f5d | |||
4bc5508f38 | |||
e503c6092e | |||
6a8985d8dd | |||
bee67fd883 | |||
a1d75d40aa | |||
29484867ca | |||
7fa983b971 | |||
617a8b2814 | |||
b924d323d4 | |||
a2efda41d3 | |||
642c114501 | |||
02dd3e457d | |||
ea7b28c9d5 | |||
472ab4a9ce | |||
fca84e3edf | |||
b70235ff92 | |||
6eff591df7 | |||
d0b2bf736e | |||
6b6443406d | |||
3452d7852a | |||
f1fa10badd | |||
1267621424 | |||
8a0ec95fe1 | |||
2de96d4dc9 | |||
a486f20892 | |||
49535deb2e | |||
7cbf62cf12 | |||
3b0ace3410 | |||
5a9c8e1d87 | |||
daaa65dc0a | |||
ab4e371524 | |||
927fd304b0 | |||
5af84b8e90 | |||
d425dac499 | |||
d056459e76 | |||
3169485f33 | |||
d9b9f80a93 | |||
d429505b71 | |||
72ee708917 | |||
93bbfac29a | |||
040d7a6563 | |||
e8dd930a50 | |||
31c049ebfe | |||
d343a37fb2 | |||
7097175c6f | |||
8e57c49043 | |||
9f036ceefd | |||
ff3ca8b36b | |||
87a7b70a27 | |||
9c71c966ca | |||
6dc99e676e | |||
80ecb82cc2 | |||
7fc9509d4d | |||
3bf5e11f94 | |||
eef9af2266 | |||
fabdf5fe30 | |||
1cc27e524b | |||
8316a002da | |||
888c637f71 | |||
48b7d2587e | |||
923c889de8 | |||
8ae575d67a | |||
b51407486a | |||
a689b34ed1 | |||
aa98e60243 | |||
c3bf767024 | |||
b641f1a230 | |||
9499685dda | |||
80d23cbbbf | |||
efa684c5e8 | |||
2edf64985d | |||
5fe7807462 | |||
e96b9005ca | |||
8c29e735e7 | |||
497e073a8c | |||
d4ce54a3c2 | |||
de37a81902 | |||
d6b8cb718a | |||
ed435d2b72 | |||
2b1f8533b0 | |||
0a21a69a9f | |||
5ebc6b698c | |||
cbc48e31e1 | |||
577dd9048f | |||
ae409dd0ec | |||
cde855e1dc | |||
adcd4368e7 | |||
8bcdb205ed | |||
2cf8b2a453 | |||
6c156380f9 | |||
369d0ee502 | |||
4971a212e9 | |||
2111a81d18 | |||
6799b3d7da | |||
a3463274ee | |||
c10e773401 | |||
f7af259576 | |||
87c6a54634 | |||
d03521bf12 | |||
3eb1919c81 | |||
e53d6dbd5c | |||
01d2db8e96 | |||
b18c2aea05 | |||
a6e3c272e2 | |||
4000f98ba4 | |||
d06fd404ae | |||
c6f0e19e2f | |||
462af9989a | |||
eedea2fdcd | |||
9c3d946de0 | |||
ace3102601 | |||
48946100e9 | |||
0067e46192 | |||
921711a679 | |||
32dfb765dd | |||
8482f12909 | |||
306a56124c | |||
1f815d7562 | |||
f25d35fad8 | |||
f74c57449e | |||
a697bd935a | |||
ec294227bd | |||
f67758eaf3 | |||
f7ed65d749 | |||
7ffeb3964b | |||
025d4df774 | |||
45086a4b6e | |||
2db0023653 | |||
bfc21220a7 | |||
507491fbec | |||
c890ef6917 | |||
6756fb4fe7 | |||
6c089c0a78 | |||
b2ab3f987c | |||
c99b2edf98 | |||
e052610184 | |||
13f1725105 | |||
f2367932e1 | |||
7e94ec986e | |||
7bda3e6994 | |||
3a18606385 | |||
e25a94e815 | |||
c13f662e2d | |||
97ee085f30 | |||
1364fd5c45 | |||
cc3186a683 | |||
0c93c4754d | |||
7b4cfbeeaa | |||
8cebb53147 | |||
3e18f2f09c | |||
add09e52ef | |||
5429a509c6 | |||
3555fa36aa | |||
ee31519552 | |||
06b41aee58 | |||
cd19d50e1d | |||
a5e4eea5ca | |||
c6a6270e16 | |||
18d9d2602a | |||
f7ec9f2073 | |||
4c5f66185d | |||
105c1893cb | |||
061cee207f | |||
af4a925b54 | |||
a6f3e87921 | |||
9764d9109f | |||
46dfa57ee0 | |||
2c861c65d4 | |||
a59bac4b40 | |||
cf214bf367 | |||
75724797f7 | |||
d04aeb55ad | |||
47bd6dc6b8 | |||
5e0f525932 | |||
1f66daf2f3 | |||
ded9cb0358 | |||
04f201933b | |||
f5ec1cb3a4 | |||
6c23e3f534 | |||
e99d54d1f6 | |||
7f436061b8 | |||
3c71200eb4 | |||
f124cf8318 | |||
9d2b944063 | |||
8e1ec5903b | |||
5cf763d51f | |||
3546859fe5 | |||
6530e45178 | |||
07f0036b2b | |||
5237f55a71 | |||
a108e5067d | |||
a4a24b1a1a | |||
ffe0eb1544 | |||
288e8a65f3 | |||
0ebfbca93e | |||
f22f57495e | |||
8786a9d21d | |||
f06a97d30b | |||
2329c47faf | |||
2967261acb | |||
64ff1ecbb6 | |||
8707f88c07 | |||
36846618ec | |||
0cb2f19e29 | |||
125a50ae87 | |||
9d37ea23f8 | |||
31617ae340 | |||
950614fb81 | |||
14bbd7b7ae | |||
257cd34101 | |||
ab6ec3a9b7 | |||
39814a89b6 | |||
24fbbf8aa8 | |||
338ceffa6d | |||
371e104b00 | |||
d5aba8eaf1 | |||
1d2b3a4ed8 | |||
f904945d40 | |||
027b2e1b88 | |||
d79eb5e1a6 | |||
f6651b03b5 | |||
5f880a179c | |||
ea03fd22db | |||
e252c9ac05 | |||
a212fb35c1 | |||
e561e4de0b | |||
1c3d5cd851 | |||
e59fbac761 | |||
332f2b0678 | |||
745ea5fb05 | |||
fa16ca4eec | |||
d7757b8b03 | |||
98aefad249 | |||
a19ba40672 | |||
3983cb001f | |||
c17222dbe4 | |||
abd8c69395 | |||
a7fde73df4 | |||
78b464b404 | |||
aa21115e26 | |||
a39f845835 | |||
3fdd8d91e2 | |||
c13bccc7ae | |||
b4f7d6bf25 | |||
fa0c2f7138 | |||
453cc2a951 | |||
bd56795c62 | |||
2c54b7f289 | |||
cd5f847b55 | |||
a25544baea | |||
39b6c5d6f4 | |||
d1c9db874f | |||
f954542dda | |||
9fec7d236c | |||
67656accf8 | |||
64952a536c | |||
65e0d5f511 | |||
903acff924 | |||
5a06946469 | |||
baef31b2c7 | |||
b9a12d1562 | |||
3f26d03166 | |||
1fed3ad532 | |||
929b245f5f | |||
0da6354825 | |||
716a28891d | |||
93a2e91694 | |||
4913dc1aad | |||
087df18fea | |||
058ce6fe82 | |||
087c10d52d | |||
18292e447c | |||
6c1dda47c0 | |||
ad1fc8f3d8 | |||
bca98269bb | |||
1bebaf933d | |||
166eb996a9 | |||
10fae34754 | |||
aa4d97e8df | |||
dbbb9d7877 | |||
82fda5cb03 | |||
3ff213b3e8 | |||
65587536ab | |||
ad31be8344 | |||
25815c81bf | |||
852a22f86d | |||
69c7f22053 | |||
75a964167a | |||
c5768c81e1 | |||
4eb2b818e7 | |||
f742aad810 | |||
e22b171b7b | |||
d061eb2c64 | |||
69aa115178 | |||
e175b87384 | |||
f216ee739a | |||
16d6644573 | |||
38afc6e6f8 | |||
8f1d214b12 | |||
51fb1a43de | |||
a86b6bfbd6 | |||
1176ddcc85 | |||
fa080e380c | |||
57c3acd9d8 | |||
302cf5b10b | |||
e2a9e81dbc | |||
b1cf7391ce | |||
9bc7521de0 | |||
a68ebd2b76 | |||
47f7c938ae | |||
842e7e559e | |||
bd5a6e6fb3 | |||
67cca3bc00 | |||
90b1609d4e | |||
abbfae2fc0 | |||
b52b854270 | |||
58b759f652 | |||
74ca756a53 | |||
a62ee7850b | |||
d3a90ccc0d | |||
46b13ee664 | |||
cfa6dc7836 | |||
f969bfa7be | |||
3576214920 | |||
f964fe3750 | |||
e86a883d0a | |||
82d764000a | |||
749c72e6a6 | |||
c3129a40f1 | |||
d04aa89812 | |||
d5f854d376 | |||
63dcb8cfe1 | |||
6c57fa078b | |||
c3cc75feff | |||
d2e6011089 | |||
5a18144366 | |||
8a0a22bfb0 | |||
950b226374 | |||
74e64a4387 | |||
59e4c1cf79 | |||
9b89ede9c4 | |||
bf205de3a1 | |||
045ad78bb9 | |||
c0350e5be7 | |||
ea7006eec4 | |||
2b3e38f77e | |||
d04fe5d582 | |||
17ab4caa5e | |||
976bc727dd | |||
484e53cc08 | |||
b09b80933d | |||
8165086d02 | |||
82f14b087a | |||
93b3419737 | |||
19290fe467 | |||
d2f679030b | |||
053bce7a8e | |||
2f208832a9 | |||
268d7495cc | |||
ce16e61e63 | |||
f92bca58fa | |||
83d541b60d | |||
965efc3a13 | |||
d656c34bd4 | |||
7f151cbeba | |||
bc2f9204e9 | |||
a922a93016 | |||
eb596ba866 | |||
2208545612 | |||
f08a875cd2 | |||
d492d3f738 | |||
c687091ce9 | |||
eb994716e6 | |||
70acc8a7c0 | |||
bf97781232 | |||
099727d671 | |||
6229cdb1ba | |||
b7a663ed20 | |||
3bd97352ba | |||
33e25d9241 | |||
fc11018158 | |||
5e22360cb1 | |||
840348b4eb | |||
450fb2553c | |||
cf04738594 | |||
03757632cf | |||
e818f5a93f | |||
ab9b08770a | |||
40df8b68ad | |||
9f5202fee3 | |||
902ccbd203 | |||
4675da4d16 | |||
86da27a7a1 | |||
fc2a6567da | |||
7c611d9b62 | |||
784c7465d1 | |||
301af7bd7a | |||
09c11a385d | |||
ef6f491d94 | |||
9dcef00fbb | |||
e781e5dd43 | |||
d3e672d811 | |||
dad1554ec2 | |||
30bf96c6cd | |||
a8c16e39b8 | |||
79a7cd2938 | |||
26562e445f | |||
0b678b1f16 | |||
79b5e85b15 | |||
2432491bfc | |||
a09ce3e026 | |||
c52fc843f6 | |||
02240bda25 | |||
0185ef7c83 | |||
7d29b9901c | |||
ae553dfed3 | |||
71c6beadb4 | |||
d939629c09 | |||
0a569146a8 | |||
d5a012d49f | |||
22a11769fa | |||
7dc7ba9977 | |||
fa4059a4b9 | |||
7f4786f9dd | |||
5a6e7a46d1 | |||
9f90749f99 | |||
0dfaf9159d | |||
5d8bda1178 | |||
9ad1e0d529 | |||
389e3397ec | |||
284b95213e | |||
952854f64e | |||
554650c18d | |||
01a2fa7c2d | |||
1257e34487 | |||
a45743f443 | |||
7d03719816 | |||
7c5bbca2fa | |||
cf313939aa | |||
873d4bd3f2 | |||
f43f3fc84b | |||
0e1fed86ba | |||
3fb5d886dc | |||
b57cd8d5c2 | |||
5c1bbc08ca | |||
6ba32b95f3 | |||
d3df113fb0 | |||
06c2ab045a | |||
6d43e0951c | |||
ec14429238 | |||
1dc2c6f183 | |||
984b8f7e6f | |||
0f8448b2c0 | |||
088c546bee | |||
20fff378f0 | |||
4994a7ac85 | |||
a959c69d32 | |||
39244568be | |||
c8fc0bb4f5 | |||
654ad5c71f | |||
7b9d18caea | |||
8d347efec2 | |||
85de6dd52e | |||
c024b39c8b | |||
1f0db48487 | |||
2bc5f475f4 | |||
5abf4c99de | |||
137e519b66 | |||
dcb27e7de8 | |||
d4d5b5a75c | |||
a83b3f8408 | |||
da9af8673f | |||
1b4ba3b396 | |||
eb2f1cbc9e | |||
28f58f72dd | |||
81389401df | |||
c618c5c5f0 | |||
0eaff4c626 | |||
9783c1052d | |||
f732fa9736 | |||
0c2d227da1 | |||
a281efef04 | |||
538dcec348 | |||
0d38c8ae8f | |||
967c1a2da9 | |||
f3da326b77 | |||
153a6e2cb0 | |||
95f37b9d36 | |||
60c37a1fc7 | |||
615c61e230 | |||
ae40b6ba8c | |||
d482427e0d | |||
c41baf3aeb | |||
dd7cb74edc | |||
4eed2c7582 | |||
100e830e04 | |||
af28d82ebc | |||
6285980f98 | |||
a5d19cd31f | |||
9c9998b468 | |||
011eb55a53 | |||
189d31cc29 | |||
d178f3d1b9 | |||
6e9d73ec64 | |||
8d1adf4f80 | |||
d0b7f58e7c | |||
19d24e5644 | |||
461f618b8a | |||
fc875651d3 | |||
5ff14d1fed | |||
80c9c1bb05 | |||
a111d9b18a | |||
df14913c67 | |||
b6c6fef770 | |||
1a2f37b0ec | |||
6f3c662783 | |||
3772137c8f | |||
0d62123a0b | |||
28fed6281f | |||
1ec95d42ba | |||
8adf965d0b | |||
026dd38480 | |||
338c2243e3 | |||
e8d61225f5 | |||
cc356ce67d | |||
364e364429 | |||
46a46877ed | |||
5ee05e3aaa | |||
5568a09f49 | |||
1199c431ff | |||
2c1a897c4e | |||
305f2fa448 | |||
b051685727 | |||
344dd92c85 | |||
4167c65acf | |||
cd6d49860f | |||
8a10fcf7ea | |||
7580bb21c3 | |||
62102236a2 | |||
3b5f96a133 | |||
ce2b711b1f | |||
667fb438cb | |||
7befa94e6d | |||
32d7835119 | |||
726abf6e65 | |||
c154a4bdc8 | |||
88ef1a3c5b | |||
5453925e26 | |||
537e314b49 | |||
ccb7a553c2 | |||
1696a5c8e1 | |||
816cf8f702 | |||
e8167541af | |||
329360aa5b | |||
eb1a276e60 | |||
a53bac1a94 | |||
93bf93d3a1 | |||
4174c8c25c | |||
48a88a8624 | |||
1442748f58 | |||
d17e216f91 | |||
56ed4fe6f2 | |||
9a71e9ba86 | |||
ef478a4a9e | |||
1bd7d40716 | |||
807e9573fb | |||
849d1d7ebd | |||
3fe2545228 | |||
090dfff730 | |||
f94e9449d5 | |||
d8753adc4e | |||
2e17ea99e2 | |||
c4daf8524e | |||
9d16898926 | |||
f4bcc1f2e5 | |||
63e8614ace | |||
5d686b146d | |||
e85758dc5f | |||
d08f090800 | |||
8554473c21 | |||
01fb1bde8b | |||
29e32ffc42 | |||
88bd60a083 | |||
48b7b725b0 | |||
8d8c932d8c | |||
253d355bd2 | |||
e287df1320 | |||
bae0bec1cc | |||
602686a5d2 | |||
af05d94198 | |||
5fa3a7ca44 | |||
9609350789 | |||
9c1e73ffff | |||
50741c70c0 | |||
fc8660df78 | |||
4e5ddca3bd | |||
3bdc90451a | |||
a036b2981a | |||
8fae83dab7 | |||
083f9dd29b | |||
7d5fabbd25 | |||
105f071847 | |||
ef68e5b13d | |||
21afe077d7 | |||
48222ce44c | |||
0922ba938c | |||
3fc66ec525 | |||
44191cd908 | |||
0da0c6bd77 | |||
b5f6e9d01b | |||
6098b196dc | |||
0922349344 | |||
53cdeeff03 | |||
fcdb086daf | |||
d2d9c2dd0f | |||
4241fb9386 | |||
cfd6751777 | |||
5e461e9b6b | |||
946dfdf7b8 | |||
4da9843479 | |||
eccb3c643d | |||
bfa5a51ce8 | |||
9066ad6cdf | |||
f7b513dff2 | |||
3de5f10d52 | |||
07429a862c | |||
2f2bddf020 | |||
ad03adaebf | |||
ac7a5488ee | |||
6de93d4fbb | |||
8a312f76c5 | |||
4a956b5a55 | |||
83d6c3ba88 | |||
52daf6b864 | |||
c8420e152f | |||
1dff19af26 | |||
a1e2eca802 | |||
48b63a26c8 | |||
b23bc4a5b6 | |||
940236b4a4 | |||
b6ef18b0d8 | |||
372484f976 | |||
086cf67e93 | |||
3c8692d06c | |||
efffca83fe | |||
04fe81e001 | |||
d2215c2ba9 | |||
89b1b6e242 | |||
e476d68848 | |||
da8835bc77 | |||
f170c2611c | |||
351b17d1d9 | |||
14e88706df | |||
926e3e2712 | |||
c39043fb9d | |||
578b3ba4f4 | |||
5b0b582039 | |||
e24be913e5 | |||
4d3358ba66 | |||
ffe40fa3a3 | |||
87f93b34a3 | |||
03bd9a5731 | |||
cb82170187 | |||
5b9e16af83 | |||
7b1b2a4bef | |||
1f1a0b7b53 | |||
320acfae89 | |||
1c171d0f12 | |||
22bf3618ea | |||
5f1593f4d0 | |||
9af75bf9b2 | |||
344fa729a5 | |||
33d3d90a93 | |||
24dfc09f35 | |||
e533bc0847 | |||
306333ceba | |||
e96312b470 | |||
fb60b4bca7 | |||
0612e4429d | |||
ee80aa26db | |||
a45e667e9c | |||
1b4a2369bb | |||
224483f6ac | |||
c61574b782 | |||
c92129ac63 | |||
6c71d95932 | |||
e859f5c7a1 | |||
dc402f5f0e | |||
188894c837 | |||
70f99a70a5 | |||
fb4fbd23d8 | |||
f58b2383b9 | |||
05caf1fe28 | |||
704486abc2 | |||
1ec023b435 | |||
fd21eeb477 | |||
edf2b2df6f | |||
9ce338622c | |||
2b35529cbd | |||
554b67a2f0 | |||
012243a880 | |||
d4a348a2b2 | |||
1d4c5cc96f | |||
41bfb96b6b | |||
994d62ac65 | |||
7c72608e1c | |||
2edc06c662 | |||
cb4d66d6fe | |||
f80602b51a | |||
58d8a5ce46 | |||
72a65218be | |||
1b0d5b710e | |||
2a25ac0847 | |||
9aefdf35a1 | |||
231961c017 | |||
ee621fa091 | |||
a69a04cfb6 | |||
b1aed344c7 | |||
3e08d665c7 | |||
4a94c86433 | |||
d4878f6ed3 | |||
d94719ea02 | |||
982b5221b1 | |||
cbdf03450d | |||
7625e591fe | |||
8fdb1e7ec9 | |||
d3b28c42e6 | |||
1b32423881 | |||
7de699c7fa | |||
e9f9670eb5 | |||
3d4e961320 | |||
db2fb33d53 | |||
ff3db04ab7 | |||
c7f6763c48 | |||
b1dd4069db | |||
58c647d433 | |||
333ea4aa53 | |||
3ad59da2a9 | |||
2d9b211eeb | |||
4f5a352985 | |||
6ae3b77c2f | |||
4a7260b1be | |||
f91c77bdc6 | |||
476e938d23 | |||
1ec9d986bb | |||
4b88cfa51a | |||
bc56226a28 | |||
a6e5474fdb | |||
8c7ca2c34d | |||
91fccc6691 | |||
93d1737357 | |||
5ba1ae9ae4 | |||
8cb408bc6e | |||
197a89a37a | |||
d336ead3b1 | |||
662644663e | |||
4c7819effb | |||
8b5b9ee8f1 | |||
89b911a9dc | |||
b673e216b6 | |||
a1b2f0ccf1 | |||
f269facf9d | |||
5a36d280d7 | |||
c39563b123 | |||
548149de8e | |||
d6d4ce0ac4 | |||
83b0239791 | |||
d1fa13d67a | |||
3abd570678 | |||
b0b0781bd7 | |||
1aa28ddee1 | |||
09b50badb1 | |||
7060108a8b | |||
e6f0d5bf44 | |||
399642f958 | |||
781effc34e | |||
324c8f8146 | |||
27e372e38f | |||
87122ce211 | |||
c0c6675423 | |||
fa4aeb5261 | |||
4e51eeb998 | |||
a27c3f09b3 | |||
3e5f117066 | |||
3753fb3ea4 | |||
d3e49cf1e9 | |||
ffcf46a371 | |||
fc5eedbef5 | |||
d78b6c4445 | |||
6e056bb337 | |||
b5c2c1009c | |||
b54029a04a | |||
e30aca7531 | |||
866722b68f | |||
d93f3468d3 | |||
83032e858a | |||
6855e314b3 | |||
2c4a8619a8 | |||
3247d83252 | |||
109ff2d8a5 | |||
48797d12eb | |||
abe66d8af0 | |||
58c9b70b26 | |||
996643bde8 | |||
68dd9a29e8 | |||
68c4b55945 | |||
bab86c9ce2 | |||
5f24e4d705 | |||
66c7b3fcb2 | |||
1ffe29c657 | |||
6b7d4877e6 | |||
80826eb500 | |||
11962facde | |||
c7eccfd804 | |||
ad617e0deb | |||
7ae70d5a4d | |||
62d1a0291e | |||
883dc72fc6 | |||
65b2e9633c | |||
3b923e0d37 | |||
4938cb9bbc | |||
a208564f06 | |||
6c6ca4daf4 | |||
1024da601d | |||
55d05ee590 | |||
66f39e070b | |||
252681001e | |||
a39014b3b1 | |||
10bcfdd6b2 | |||
41ea9814e8 | |||
bccf7e3f69 | |||
06c8a004d8 | |||
4ab90fb14d | |||
54d8feeaf0 | |||
23d20c918f | |||
10012d7125 | |||
d70ea854b1 | |||
8c576ece28 | |||
e0c0935d3a | |||
ab987e73c6 | |||
cec203a6db | |||
cf8bb9efb0 | |||
201a053025 | |||
703f987825 | |||
4e3d677d8e | |||
aab90130d2 | |||
9321dfdd89 | |||
2607ef5fe0 | |||
84b291a57a | |||
3271c5a60b | |||
79bb66f535 | |||
1616885e7f | |||
85045c3e9c | |||
4a85296e23 | |||
4710d18e33 | |||
3da75494b0 | |||
ac021f38da | |||
d0a2c36f6c | |||
9facc9379f | |||
a0f4e2659b | |||
4dfa3d8372 | |||
30563ee04c | |||
3fa7594fcc | |||
deac32d843 | |||
ccfa32ce93 | |||
f05d114f05 | |||
fcb41e30dc | |||
1cdb456216 | |||
5ed6bba412 | |||
50e929eb0c | |||
129839ce21 | |||
fd2961ecb9 | |||
e506d988e2 | |||
196649c0e9 | |||
12182ee04f | |||
5db64526cc | |||
5c2ec70eb4 | |||
24a2c6251f | |||
0d035d9ae9 | |||
a28f1294e2 | |||
a3b0cde59d | |||
c2dec9eac4 | |||
0fdb49f168 | |||
e86d02765f | |||
6e877aea02 | |||
330f1577fd | |||
ff980591c3 | |||
60992ae492 | |||
1b439c15f6 | |||
f58c16ab03 | |||
b560d2953f | |||
2c52c8efb7 | |||
5375166f04 | |||
5e85cae9ce | |||
0ae449fd44 | |||
4f5b4f387a | |||
fcd3c39656 | |||
b4c68a8ae5 | |||
a8d3d613b0 | |||
c680fbe834 | |||
34023f66f0 | |||
2696da7337 | |||
4edbd8719c | |||
ed6c59b58a | |||
553525a0fb | |||
38ebb95e63 | |||
17d92d8b79 | |||
4c08c692ea | |||
286c77778f | |||
10c4bee1e5 | |||
f26b8ee224 | |||
1961567ebd | |||
094687717d | |||
e5844c926b | |||
96ad8c823a | |||
de2977284c | |||
32bafd8b01 | |||
c1dea44fa6 | |||
edb4b9a82e | |||
008b4228d2 | |||
5a105eb2b3 | |||
faf455a37c | |||
7a076f7304 | |||
828a7aabd7 | |||
acbf2a8ab0 | |||
05a4701d98 | |||
6a19b333b7 | |||
864fc84899 | |||
cddd62c284 | |||
30c46c0858 | |||
396e54bad0 | |||
a2b9ee5988 | |||
fac8e8aa8b | |||
a821b309f7 | |||
bd2b627113 | |||
3c3711b933 | |||
926ffefac8 | |||
986b303f2f | |||
afd8717c21 | |||
6effd783c0 | |||
b8f47545ed | |||
a10c4f7a34 | |||
939dd0b207 | |||
defabf4324 | |||
a6394b2dce | |||
5ba802dc68 | |||
b652d0fc65 | |||
e67843638f | |||
ca704e1d51 | |||
04eb356c89 | |||
300159c03b | |||
f083b816a9 | |||
2a46f6b225 | |||
b3e9b266fa | |||
76c72c1a7f | |||
1c1cf58409 | |||
8155e3ef7a | |||
d9fa2c4a62 | |||
547e640b57 | |||
e107037011 | |||
62048c68f0 | |||
489aae7a46 | |||
1bcb6738bb | |||
d14a13fcaf | |||
5facba4419 | |||
f32df2ac9c | |||
22a0b3be45 | |||
b4282a03ca | |||
3490d0d743 | |||
688659b815 | |||
37cf9eb587 | |||
65b2da4db5 | |||
2cecb11847 | |||
fbe829def7 | |||
a39e0a19cd | |||
50d805abbc | |||
b7467b466f | |||
3048a26e6f | |||
0175d7658e | |||
5eee528d6b | |||
4a431ddc7c | |||
ebda485bcb | |||
f9bb55bc5c | |||
5d207f36e6 | |||
6fc9098035 | |||
215c3d82e2 | |||
86d0feed32 | |||
53674b03fc | |||
102e454902 | |||
88d59eb7fa | |||
53ebd583cf | |||
a1914f5079 | |||
5231eb62e1 | |||
661cf440f3 | |||
3822058daf | |||
011f283067 | |||
4f58a485a6 | |||
858a1c7ae0 | |||
0c96510128 | |||
ecf7860847 | |||
161eea3e9e | |||
27e2699fa1 | |||
906d90c304 | |||
d243bf069e | |||
dde3d5c35b | |||
d1e792686e | |||
62fe380520 | |||
319f08c4c9 | |||
657129e4a7 | |||
5bbef09f85 | |||
342f5e5e41 | |||
02c0bac71f | |||
9bb091d31e | |||
a0e201a9ef | |||
119d5ba7ff | |||
a35454b1b3 | |||
8cb340be9d | |||
8d21ee23f4 | |||
5e7c376950 | |||
7617d56276 | |||
80e4b33047 | |||
4f6287c163 | |||
84ee1a2d25 | |||
67252e0c6b | |||
4264c2e266 | |||
0efa4ffb23 | |||
0d13fe67b0 | |||
ae108bb603 | |||
ca4229c732 | |||
5c827703a1 | |||
a3de0820b3 | |||
83cb473a45 | |||
e7f9db5e56 | |||
af3de448bd | |||
fcb2f1b555 | |||
c1bcf9fa8a | |||
5ddfe7a184 | |||
c675caf3f9 | |||
956b3d89db | |||
a0c6e9e490 | |||
b0c15bc430 | |||
56960d6da9 | |||
b934a6b6e9 | |||
df73be495e | |||
efca13c8c0 | |||
e49b0b9381 | |||
a69cd85ed7 | |||
7b520942dc | |||
7ee00230fd | |||
bc8c7285da | |||
12e6baa925 | |||
f98225cdb6 | |||
310abcd8b9 | |||
f42eaaea86 | |||
bfdb74979f | |||
2590dc690e | |||
e2545b3d34 | |||
28e002e248 | |||
7d12dbd4b2 | |||
6f60e71ea4 | |||
16c842366a | |||
97ba151e09 | |||
18f452d968 | |||
bb6db783d8 | |||
49ce302bd4 | |||
95f01007a3 | |||
bf4d920fb4 | |||
d84fb244d3 | |||
f04b5244fa | |||
6f3829fa91 | |||
a5ae3545d5 | |||
63b658ac50 | |||
e8544be30a | |||
39090451f0 | |||
12fc6e6354 | |||
154326061d | |||
0bbfd574b3 | |||
eff3887f35 | |||
34cb532e6b | |||
cf1d1a97f3 | |||
e0a4f0bc4c | |||
1ac8eba1b8 | |||
82f8c31b81 | |||
9f2ae75fa2 | |||
9d220c482d | |||
c4718bd302 | |||
e1e2a90498 | |||
40a48954c3 | |||
e81068f528 | |||
0591487cfd | |||
737ed7ba5d | |||
27fd1f39f0 | |||
1210313e7f | |||
4733c8fb75 | |||
8777e65996 | |||
330fa10935 | |||
1bd4a0f4ee | |||
bea8ae55de | |||
b99cd49091 | |||
35f3b70968 | |||
e33f3231d0 | |||
b6cf6ee94a | |||
6118518b1b | |||
f77d5ebfd2 | |||
fcf5c41709 | |||
7f3279f20f | |||
c23de50f3e | |||
51f1759323 | |||
cedc634933 | |||
c7b89a1126 | |||
bad015de9a | |||
21057759de | |||
d027352d79 | |||
fd8c568d75 | |||
85292ca1b4 | |||
1ab90e76d1 | |||
1e9e9daeb3 | |||
65682a8130 | |||
bca225e692 | |||
4fbc46b3b3 | |||
4b3806fb9a | |||
5abbec94c4 | |||
346d1dddba | |||
8b48ad77e8 | |||
64d97a3232 | |||
94a208bd91 | |||
b15ac8b13e | |||
0ddff9d551 | |||
7c31dbb3d6 | |||
6ee995afaa | |||
691acb647e | |||
80d9d88de1 | |||
cf12abfc7f | |||
108e516b80 | |||
b21ec7a302 | |||
972473f1af | |||
ee8e0b46a2 | |||
1c3d270402 | |||
9d09d2042a | |||
b8823ade29 | |||
0e5b01f897 | |||
87f221290f | |||
fd3b0c20b6 | |||
93e71027fd | |||
1c5097b81b | |||
ef1bbda49c | |||
c6852c70fd | |||
953773aaa8 | |||
e0a139d083 | |||
867140df30 | |||
8a354e6187 | |||
5fed14cb78 | |||
7e7c110851 | |||
5337153761 | |||
444834a891 | |||
2587727087 | |||
a6456b068d | |||
4ccf26c23f | |||
3927dfa71d | |||
31c324bcc3 | |||
476d6fe85d | |||
ee4d468bce | |||
47fca55b0c | |||
6afd4b0dc7 | |||
e2517ef50e | |||
ad6798eaad | |||
2a5fcc0846 | |||
37592a876e | |||
4979e176c7 | |||
f3fdac2762 | |||
9a67617cef | |||
96e43cbb65 | |||
458dc5b232 | |||
28a7c6d3aa | |||
d3c241d283 | |||
445959abbd | |||
62842968cb | |||
2dda683aa6 | |||
616167e08c | |||
acd74c60c8 | |||
c1c4a2933e | |||
8158ead1a2 | |||
fcc196f452 | |||
d55e2492d4 | |||
45e05b0891 | |||
eb53739c95 | |||
219f310a25 | |||
4a35059711 | |||
27071cfa29 | |||
a1ad1147e6 | |||
eefa7d53c5 | |||
716491a68e | |||
29e5263fcc | |||
4df61d7efc | |||
34aa60d47b | |||
1d88a5b42e | |||
74a9c46f08 | |||
7a540f2a88 | |||
9f48d5e5ff | |||
64cc2567bd | |||
3b47eb3b07 | |||
d13e08e53b | |||
4685461282 | |||
885759abc5 | |||
0c0c8e503e | |||
5605cfe213 | |||
3e3fc54da4 | |||
e59c66ae26 | |||
d74eef8088 | |||
88a240b0f6 | |||
ee21f41b25 | |||
9ec2010ac2 | |||
e928fee26f | |||
79f6723678 | |||
db1fbad0db | |||
812a0a14fc | |||
852875b440 | |||
d1dd1b8a9b | |||
30974482c5 | |||
982696fb3b | |||
cf43dc7b5c | |||
4444525c01 | |||
760cc89449 | |||
ba26f22f53 | |||
e1f37a2f3c | |||
e59287d736 | |||
5cda0c7684 | |||
717a1d8f57 | |||
a955730086 | |||
d5ccee7bbb | |||
97e2a17ce1 | |||
a32a58bd0f | |||
093201ef65 | |||
ef46603f4e | |||
3483f63b72 | |||
8bee8060f9 | |||
db58c9aca9 | |||
75f5ec8575 | |||
246ceebe0e | |||
a294b128c7 | |||
7879bf19eb | |||
80082d9c26 | |||
9fe1709bf7 | |||
fec21f1208 | |||
1d4b34c0dd | |||
d88e0f16ac | |||
8d31c474df | |||
704d545159 | |||
a1e5a2cb67 | |||
3668c87e0d | |||
28c8dc4bcc | |||
a02915dadb | |||
c916f46ac3 | |||
5230fbaf6e | |||
813e65e586 | |||
387605f443 | |||
ff335ecadd | |||
74ccee2aa4 | |||
4976f35979 | |||
9c09a4d393 | |||
905bcd8d1b | |||
6883618825 | |||
8beaf2107e | |||
cd1db214b0 | |||
a6b4d59d94 | |||
ff590d3090 | |||
c16e425980 | |||
622322c878 | |||
7c580e276a | |||
927013cd57 | |||
666bf0ebb4 | |||
c283d3181f | |||
28dfe2140c | |||
75ac3450e6 | |||
f727dd26ed | |||
d21c0bfc18 | |||
65b2c056c6 | |||
53533e71e9 | |||
90a28732af | |||
98b1e50c86 | |||
512ffa9030 | |||
dbd37a0961 | |||
4eaba01de0 | |||
09cdbe6b90 | |||
0d33964a03 | |||
b14523ecfa | |||
38fa083503 | |||
fff050ef14 | |||
1a10c60e4f | |||
eb6d19a4dc | |||
9d92174b1d | |||
0dd38870e0 | |||
964d752e11 | |||
a4305540f0 | |||
788dcbf471 | |||
a715022049 | |||
c9fe2e8a66 | |||
5170f508f7 | |||
72900eaf93 | |||
85b6540c9f | |||
d92fb1ec95 | |||
9051bf6e68 | |||
598de3697d | |||
ff515f9bb0 | |||
10ed23e144 | |||
253e75c747 | |||
6efbe62dca | |||
0eae17075f | |||
c7c47635f7 | |||
8e1445d27a | |||
0fc92942cf | |||
2628a061f7 | |||
78971ac504 | |||
107a0e1b7d | |||
fc6954a541 | |||
2f60afb039 | |||
1a2da16e12 | |||
678e0912ae | |||
8d596c07df | |||
dc9f6013e8 | |||
e5a21fda32 | |||
2a0f920bcd | |||
dbd2f2003d | |||
4922877816 | |||
e914378dd9 | |||
9ec35d2bfe | |||
d96cc79814 | |||
7b92703624 | |||
22089c528a | |||
20ebdc46e9 | |||
3224cd73ed | |||
024c7f6a15 | |||
96d4b52da3 | |||
d1e29b8a9d | |||
0e02714114 | |||
04ad3c0386 | |||
14985bcdcd | |||
a1908de302 | |||
8820f10e01 | |||
e1116938ec | |||
c30678af98 | |||
1d4e06b884 | |||
39d6fcac73 | |||
f44c1d4536 | |||
c4349951da | |||
5a42704ac4 | |||
52e94fe650 | |||
466b9da56c | |||
b280288e83 | |||
7388c13c63 | |||
b4f4ccec99 | |||
e5e3f02440 | |||
874bfa0c54 | |||
92a4f7adb8 | |||
0772417f33 | |||
14f1b6df4b | |||
0d9c8a804d | |||
0dfbfafb82 | |||
051ef564e7 | |||
bafd3612e6 | |||
05f9f7ce9d | |||
88acf49305 | |||
74d9901ec9 | |||
64d1b56497 | |||
0cc540b12a | |||
a1712a654d | |||
fe21889ab0 | |||
e50c84ff28 | |||
44824acf34 | |||
ec3253620e | |||
f902466882 | |||
b5d2a23c64 | |||
0fd4804f95 | |||
729d0ea417 | |||
8f2f644230 | |||
183edf9eaf | |||
46c37403a6 | |||
c57e15bdf1 | |||
bc0e53a59b | |||
2c38b51996 | |||
67e36788b0 | |||
9b8ed32c74 | |||
833063c916 | |||
3b2c8e0a97 | |||
ee90d1f258 | |||
f90f42c25c | |||
d6555cb344 | |||
286f057a14 | |||
00b89c2bc7 | |||
b4a3de4cff | |||
ec49c96219 | |||
1eb420bcda | |||
9ba810ccb6 | |||
7a99241c76 | |||
617066c7e1 | |||
835dd4da9d | |||
15da928655 | |||
7460e6f73b | |||
4104b4d0a3 | |||
a29259a8b6 | |||
8925c6caf9 | |||
7b2a85a118 | |||
f8980aecf0 | |||
a4f44f02ed | |||
3fe76a6bd3 | |||
8c060b468b | |||
6136039682 | |||
f2954eeb3c | |||
f186c41f4d | |||
7d69f4d3ed | |||
4baec1b185 | |||
90c4361363 | |||
10aaa48068 | |||
458b0150ef | |||
44a3f63f99 | |||
96a6b11ab4 | |||
9ef9ce76f8 | |||
94835c46a0 | |||
58b3d31526 | |||
b023f5c0da | |||
78b87c6ddd | |||
edde4dc2fa | |||
d9a6e41265 | |||
64f0f1aa2c | |||
b54c057c83 | |||
baa4acaf79 | |||
3c6bb41939 | |||
3f64d3729e | |||
21dc2ece1b | |||
fcac6c4f8c | |||
e5dc932717 | |||
6b65b05e2f | |||
d52b973b44 | |||
79d3f4ca9e | |||
06dd22d89a | |||
e79e425cf5 | |||
b4b2c351b4 | |||
73acaadf70 | |||
18ef36bbc3 | |||
021315f0f5 | |||
a4ee103ff0 | |||
618173c5f0 | |||
4cb906571b | |||
6092b6c4cf | |||
9bf17a1c8d | |||
542379dcf4 | |||
a565bb5889 | |||
9a96ff2edc | |||
9fa2e363cc | |||
a9939a31cf | |||
64fffbcdec | |||
a65f8f5d5c | |||
c5475fb028 | |||
f267c46595 | |||
b1f67a9a65 | |||
044a7524a3 | |||
495b15e065 | |||
f4e6c399f2 | |||
4519acb77e | |||
cf1ba6d459 | |||
c28cb67484 | |||
9017ee9a40 | |||
52086a2d39 | |||
facec59fe8 | |||
75eb79bd55 | |||
307209945c | |||
8db9f40001 | |||
472b8d0e51 | |||
dbebd32a6e | |||
ad8d2a913b | |||
38bd247d64 | |||
6a4e972de6 | |||
cc14ac0bac | |||
2e0d7fdbb8 | |||
2284eea2d8 | |||
867b5b2ee4 | |||
85e29fffc9 | |||
fa84d812f1 | |||
4ffa8420dd | |||
7dc1c54578 | |||
05434d3575 | |||
ddea9b9f38 | |||
9445ee41cf | |||
8325b4e5aa | |||
1ff9db3714 | |||
12ff102a21 | |||
f660111751 | |||
bac08306fb | |||
c7713f559d |
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
ko_fi: cmdr2_stablediffusion_ui
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS:
|
||||
- Browser:
|
||||
- Version:
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device:
|
||||
- OS:
|
||||
- Browser
|
||||
- Version
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
4
.gitignore
vendored
@ -1 +1,5 @@
|
||||
__pycache__
|
||||
installer
|
||||
installer.tar
|
||||
dist
|
||||
.idea/*
|
||||
|
27
3rd-PARTY-LICENSES
Normal file
@ -0,0 +1,27 @@
|
||||
jquery-confirm
|
||||
==============
|
||||
https://craftpip.github.io/jquery-confirm/
|
||||
|
||||
jquery-confirm is licensed under the MIT license:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Boniface Pereira
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
48
CHANGES.md
Normal file
@ -0,0 +1,48 @@
|
||||
# What's new?
|
||||
|
||||
## v2.4
|
||||
### Major Changes
|
||||
- **Automatic scanning for malicious model files** - using `picklescan`. Thanks @JeLuf
|
||||
- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder
|
||||
- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs
|
||||
- **Cleaner UI design** - Show settings and help in new tabs, instead of dropdown popups (which were buggy). Thanks @mdiller
|
||||
- **Progress bar.** Thanks @mdiller
|
||||
- **Custom Image Modifiers** - You can now save your custom image modifiers! Your saved modifiers can include special characters like `{}, (), [], |`
|
||||
- Drag and Drop **text files generated from previously saved images**, and copy settings to clipboard. Thanks @madrang
|
||||
- Paste settings from clipboard. Thanks @JeLuf
|
||||
- Bug fixes to reduce the chances of tasks crashing during long multi-hour runs (chrome can put long-running background tabs to sleep). Thanks @JeLuf and @madrang
|
||||
- **Improved documentation.** Thanks @JeLuf and @jsuelwald
|
||||
- Improved the codebase for dealing with system settings and UI settings. Thanks @mdiller
|
||||
- Help instructions next to some setttings, and in the tab
|
||||
- Show system info in the settings tab
|
||||
- Keyboard shortcut: Ctrl+Enter to start a task
|
||||
- Configuration to prevent the browser from opening on startup
|
||||
- Lots of minor bug fixes
|
||||
- A `What's New?` tab in the UI
|
||||
- Ask for a confimation before clearing the results pane or stopping a render task. The dialog can be skipped by holding down the shift key while clicking on the button.
|
||||
- Show the network addresses of the server in the systems setting dialog
|
||||
|
||||
### Detailed changelog
|
||||
* 2.4.17 - 30 Nov 2022 - Scroll to generated image. Thanks @patriceac
|
||||
* 2.4.17 - 30 Nov 2022 - Show the network addresses of the server in the systems setting dialog. Thanks @JeLuf
|
||||
* 2.4.17 - 30 Nov 2022 - Fix a bug where GFPGAN wouldn't work properly when multiple GPUs tried to run it at the same time. Thanks @madrang
|
||||
* 2.4.17 - 30 Nov 2022 - Confirm before stopping or clearing all the tasks. Thanks @JeLuf
|
||||
* 2.4.16 - 29 Nov 2022 - Bug fixes for SD 2.0 - remove the need for patching, default to SD 1.4 model if trying to load an SD2 model in SD1.4.
|
||||
* 2.4.15 - 25 Nov 2022 - Experimental support for SD 2.0. Uses lots of memory, not optimized, probably GPU-only.
|
||||
* 2.4.14 - 22 Nov 2022 - Change the backend to a custom fork of Stable Diffusion
|
||||
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
|
||||
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
||||
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
||||
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
||||
* 2.4.11 - 19 Nov 2022 - New system settings to manage the network config (port number and whether to only listen on localhost)
|
||||
* 2.4.11 - 19 Nov 2022 - Address a regression in how long images take to generate. Use the previous code for moving a model to CPU. This improves things by a second or two per image, but we still have a regression (investigating).
|
||||
* 2.4.10 - 18 Nov 2022 - Textarea for negative prompts. Thanks @JeLuf
|
||||
* 2.4.10 - 18 Nov 2022 - Improved design for Settings, and rounded toggle buttons instead of checkboxes for a more modern look. Thanks @mdiller
|
||||
* 2.4.9 - 18 Nov 2022 - Add Picklescan - a scanner for malicious model files. If it finds a malicious file, it will halt the web application and alert the user. Thanks @JeLuf
|
||||
* 2.4.8 - 18 Nov 2022 - A `Use these settings` button to use the settings from a previously generated image task. Thanks @patriceac
|
||||
* 2.4.7 - 18 Nov 2022 - Don't crash if a VAE file fails to load
|
||||
* 2.4.7 - 17 Nov 2022 - Fix a bug where Face Correction (GFPGAN) would fail on cuda:N (i.e. GPUs other than cuda:0), as well as fail on CPU if the system had an incompatible GPU.
|
||||
* 2.4.6 - 16 Nov 2022 - Fix a regression in VRAM usage during startup, which caused 'Out of Memory' errors when starting on GPUs with 4gb (or less) VRAM
|
||||
* 2.4.5 - 16 Nov 2022 - Add checkbox for "Open browser on startup".
|
||||
* 2.4.5 - 16 Nov 2022 - Add a directory for core plugins that ship with Stable Diffusion UI by default.
|
||||
* 2.4.5 - 16 Nov 2022 - Add a "What's New?" tab as a core plugin, which fetches the contents of CHANGES.md from the app's release branch.
|
51
CONTRIBUTING.md
Normal file
@ -0,0 +1,51 @@
|
||||
Hi there, these instructions are meant for the developers of this project.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file. In that case, please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
Thanks
|
||||
|
||||
# For developers:
|
||||
|
||||
If you would like to contribute to this project, there is a discord for dicussion:
|
||||
[](https://discord.com/invite/u9yhsFmEkB)
|
||||
|
||||
## Development environment for UI (frontend and server) changes
|
||||
This is in-flux, but one way to get a development environment running for editing the UI of this project is:
|
||||
(swap `.sh` or `.bat` in instructions depending on your environment, and be sure to adjust any paths to match where you're working)
|
||||
|
||||
1) Install the project to a new location using the [usual installation process](https://github.com/cmdr2/stable-diffusion-ui#installation), e.g. to `/projects/stable-diffusion-ui-archive`
|
||||
2) Start the newly installed project, and check that you can view and generate images on `localhost:9000`
|
||||
3) Next, please clone the project repository using `git clone` (e.g. to `/projects/stable-diffusion-ui-repo`)
|
||||
4) Close the server (started in step 2), and edit `/projects/stable-diffusion-ui-archive/scripts/on_env_start.sh` (or `on_env_start.bat`)
|
||||
5) Comment out the lines near the bottom that copies the `files/ui` folder, e.g:
|
||||
|
||||
for `.sh`
|
||||
```
|
||||
# rm -rf ui
|
||||
# cp -Rf sd-ui-files/ui .
|
||||
# cp sd-ui-files/scripts/on_sd_start.sh scripts/
|
||||
# cp sd-ui-files/scripts/start.sh .
|
||||
```
|
||||
for `.bat`
|
||||
```
|
||||
REM @xcopy sd-ui-files\ui ui /s /i /Y
|
||||
REM @copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
REM @copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
```
|
||||
6) Next, comment out the line at the top of `/projects/stable-diffusion-ui-archive/scripts/on_sd_start.sh` (or `on_sd_start.bat`) that copies `on_env_start`. For e.g. `@rem @copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y`
|
||||
8) Delete the current `ui` folder at `/projects/stable-diffusion-ui-archive/ui`
|
||||
9) Now make a symlink between the repository clone (where you will be making changes) and this archive (where you will be running stable diffusion):
|
||||
`ln -s /projects/stable-diffusion-ui-repo/ui /projects/stable-diffusion-ui-archive/ui`
|
||||
or for Windows
|
||||
`mklink /J \projects\stable-diffusion-ui-archive\ui \projects\stable-diffusion-ui-repo\ui` (link name first, source repo dir second)
|
||||
9) Run the project again (like in step 2) and ensure you can still use the UI.
|
||||
10) Congrats, now any changes you make in your repo `ui` folder are linked to this running archive of the app and can be previewed in the browser.
|
||||
11) Please update CHANGES.md in your pull requests.
|
||||
|
||||
Check the `ui/frontend/build/README.md` for instructions on running and building the React code.
|
||||
|
||||
## Development environment for Installer changes
|
||||
Build the Windows installer using Windows, and the Linux installer using Linux. Don't mix the two, and don't use WSL. An Ubuntu VM is fine for building the Linux installer on a Windows host.
|
||||
|
||||
1. Run `build.bat` or `./build.sh` depending on whether you're in Windows or Linux.
|
||||
2. Make a new GitHub release and upload the Windows and Linux installer builds created inside the `dist` folder.
|
15
Dockerfile
@ -1,15 +0,0 @@
|
||||
FROM python:3.9
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt update
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "9000"]
|
24
How to install and run.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Congrats on downloading Stable Diffusion UI, version 2!
|
||||
|
||||
If you haven't downloaded Stable Diffusion UI yet, please download from https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
After downloading, to install please follow these instructions:
|
||||
|
||||
For Windows:
|
||||
- Please double-click the "Start Stable Diffusion UI.cmd" file inside the "stable-diffusion-ui" folder.
|
||||
|
||||
For Linux:
|
||||
- Please open a terminal, and go to the "stable-diffusion-ui" directory. Then run ./start.sh
|
||||
|
||||
That file will automatically install everything. After that it will start the Stable Diffusion interface in a web browser.
|
||||
|
||||
To start the UI in the future, please run the same command mentioned above.
|
||||
|
||||
|
||||
If you have any problems, please:
|
||||
1. Try the troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
||||
2. Or, seek help from the community at https://discord.com/invite/u9yhsFmEkB
|
||||
3. Or, file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
|
||||
|
||||
Thanks
|
||||
cmdr2 (and contributors to the project)
|
1
NSIS/README.md
Normal file
@ -0,0 +1 @@
|
||||
Scripts to be used with the Nullsoft Scriptable Installation System
|
BIN
NSIS/astro.bmp
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
NSIS/sd.ico
Normal file
After Width: | Height: | Size: 200 KiB |
265
NSIS/sdui.nsi
Normal file
@ -0,0 +1,265 @@
|
||||
; Script generated by the HM NIS Edit Script Wizard.
|
||||
|
||||
Target x86-unicode
|
||||
Unicode True
|
||||
!AddPluginDir /x86-unicode "."
|
||||
; HM NIS Edit Wizard helper defines
|
||||
!define PRODUCT_NAME "Stable Diffusion UI"
|
||||
!define PRODUCT_VERSION "Installer 2.35"
|
||||
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
|
||||
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
|
||||
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Cmdr2\App Paths\installer.exe"
|
||||
|
||||
; MUI 1.67 compatible ------
|
||||
!include "MUI.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "nsDialogs.nsh"
|
||||
|
||||
Var Dialog
|
||||
Var Label
|
||||
Var Button
|
||||
|
||||
Var InstDirLen
|
||||
Var LongPathsEnabled
|
||||
Var AccountType
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; This function returns the number of spaces in a string.
|
||||
; The string is passed on the stack (using Push $STRING)
|
||||
; The result is also returned on the stack and can be consumed with Pop $var
|
||||
; https://nsis.sourceforge.io/Check_for_spaces_in_a_directory_path
|
||||
Function CheckForSpaces
|
||||
Exch $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
StrCpy $R1 -1
|
||||
StrCpy $R3 $R0
|
||||
StrCpy $R0 0
|
||||
loop:
|
||||
StrCpy $R2 $R3 1 $R1
|
||||
IntOp $R1 $R1 - 1
|
||||
StrCmp $R2 "" done
|
||||
StrCmp $R2 " " 0 loop
|
||||
IntOp $R0 $R0 + 1
|
||||
Goto loop
|
||||
done:
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; The function DirectoryLeave is called after the user chose the installation directory.
|
||||
; If it calls "abort", the user is sent back to choose a different directory.
|
||||
Function DirectoryLeave
|
||||
; check whether the installation directory path is longer than 30 characters.
|
||||
; If yes, we suggest to the user to enable long filename support
|
||||
;----------------------------------------------------------------------------
|
||||
StrLen $InstDirLen "$INSTDIR"
|
||||
|
||||
; Check whether the registry key that allows for >260 characters in a path name is set
|
||||
ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled"
|
||||
|
||||
${If} $InstDirLen > 30
|
||||
${AndIf} $LongPathsEnabled == "0"
|
||||
; Check whether we're in the Admin group
|
||||
UserInfo::GetAccountType
|
||||
Pop $AccountType
|
||||
|
||||
${If} $AccountType == "Admin"
|
||||
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION 'The path name is too long. $\n$\nYou can either enable long file name support in Windows,$\nor you can go back and choose a different path.$\n$\nFor details see: shorturl.at/auBD1$\n$\nEnable long path name support in Windows?' IDYES`
|
||||
; Enable long path names
|
||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation path name too long. The installation path must not have more than 30 characters."
|
||||
abort
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
; Check for spaces in the installation directory path.
|
||||
; ----------------------------------------------------
|
||||
|
||||
; $R0 = CheckForSpaces( $INSTDIR )
|
||||
Push $INSTDIR # Input string (install path).
|
||||
Call CheckForSpaces
|
||||
Pop $R0 # The function returns the number of spaces found in the input string.
|
||||
|
||||
; Check if any spaces exist in $INSTDIR.
|
||||
${If} $R0 != 0
|
||||
; Plural if more than 1 space in $INSTDIR.
|
||||
; If $R0 == 1: $R1 = ""; else: $R1 = "s"
|
||||
StrCmp $R0 1 0 +3
|
||||
StrCpy $R1 ""
|
||||
Goto +2
|
||||
StrCpy $R1 "s"
|
||||
|
||||
; Show message box then take the user back to the Directory page.
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Error: The Installaton directory \
|
||||
has $R0 space character$R1.$\nPlease choose an installation directory without space characters."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Check for NTFS filesystem. Installations on FAT fail.
|
||||
; -----------------------------------------------------
|
||||
StrCpy $5 $INSTDIR 3
|
||||
System::Call 'Kernel32::GetVolumeInformation(t "$5",t,i ${NSIS_MAX_STRLEN},*i,*i,*i,t.r1,i ${NSIS_MAX_STRLEN})i.r0'
|
||||
${If} $0 <> 0
|
||||
${AndIf} $1 == "NTFS"
|
||||
MessageBox mb_ok "$5 has filesystem type '$1'.$\nOnly NTFS filesystems are supported.$\nPlease choose a different drive."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Open the MS download page in a browser and enable the [Next] button
|
||||
Function MSMediaFeaturepack
|
||||
ExecShell "open" "https://www.microsoft.com/en-us/software-download/mediafeaturepack"
|
||||
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 1
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Install the MS Media Feature Pack, if it is missing (e.g. on Windows 10 N)
|
||||
Function MediaPackDialog
|
||||
!insertmacro MUI_HEADER_TEXT "Windows Media Feature Pack" "Required software module is missing"
|
||||
|
||||
; Skip this dialog if mf.dll is installed
|
||||
${If} ${FileExists} "$WINDIR\system32\mf.dll"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $Dialog
|
||||
|
||||
${If} $Dialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
${NSD_CreateLabel} 0 0 100% 48u "The Windows Media Feature Pack is missing on this computer. It is required for the Stable Diffusion UI.$\nYou can continue the installation after installing the Windows Media Feature Pack."
|
||||
Pop $Label
|
||||
|
||||
${NSD_CreateButton} 10% 49u 80% 12u "Download Meda Feature Pack from Microsoft"
|
||||
Pop $Button
|
||||
|
||||
GetFunctionAddress $0 MSMediaFeaturePack
|
||||
nsDialogs::OnClick $Button $0
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 0
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI Settings
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "sd.ico"
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "astro.bmp"
|
||||
|
||||
; Welcome page
|
||||
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Stable Diffusion UI.$\n$\n\
|
||||
Click Next to continue."
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
Page custom MediaPackDialog
|
||||
|
||||
; License page
|
||||
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
|
||||
; Directory page
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
; Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Finish page
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI end
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "Install Stable Diffusion UI.exe"
|
||||
InstallDir "C:\Stable-Diffusion-UI\"
|
||||
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
|
||||
ShowInstDetails show
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; List of files to be installed
|
||||
Section "MainSection" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
File "..\CreativeML Open RAIL-M License"
|
||||
File "..\How to install and run.txt"
|
||||
File "..\LICENSE"
|
||||
File "..\Start Stable Diffusion UI.cmd"
|
||||
SetOutPath "$INSTDIR\scripts"
|
||||
File "..\scripts\bootstrap.bat"
|
||||
File "..\scripts\install_status.txt"
|
||||
File "..\scripts\on_env_start.bat"
|
||||
File "C:\windows\system32\curl.exe"
|
||||
CreateDirectory "$INSTDIR\profile"
|
||||
CreateDirectory "$SMPROGRAMS\Stable Diffusion UI"
|
||||
CreateShortCut "$SMPROGRAMS\Stable Diffusion UI\Start Stable Diffusion UI.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
SectionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Our installer only needs 25 KB, but once it has run, we need 25 GB
|
||||
; So we need to overwrite the automatically detected space requirements.
|
||||
; https://nsis.sourceforge.io/Docs/Chapter4.html#4.9.13.7
|
||||
; The example in section 4.9.13.7 seems to be wrong: the number
|
||||
; needs to be provided in Kilobytes.
|
||||
Function .onInit
|
||||
; Set required size of section 'SEC01' to 25 Gigabytes
|
||||
SectionSetSize ${SEC01} 26214400
|
||||
|
||||
|
||||
; Check system meory size. We need at least 8GB
|
||||
; ----------------------------------------------------
|
||||
|
||||
; allocate a few bytes of memory
|
||||
System::Alloc 64
|
||||
Pop $1
|
||||
|
||||
; Retrieve HW info from the Windows Kernel
|
||||
System::Call "*$1(i64)"
|
||||
System::Call "Kernel32::GlobalMemoryStatusEx(i r1)"
|
||||
; unpack the data into $R2 - $R10
|
||||
System::Call "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
|
||||
|
||||
# free up the memory
|
||||
System::Free $1
|
||||
|
||||
; Result mapping:
|
||||
; "Structure size: $2 bytes"
|
||||
; "Memory load: $3%"
|
||||
; "Total physical memory: $4 bytes"
|
||||
; "Free physical memory: $5 bytes"
|
||||
; "Total page file: $6 bytes"
|
||||
; "Free page file: $7 bytes"
|
||||
; "Total virtual: $8 bytes"
|
||||
; "Free virtual: $9 bytes"
|
||||
|
||||
; Mem size in MB
|
||||
System::Int64Op $4 / 1048576
|
||||
Pop $4
|
||||
|
||||
${If} $4 < "8000"
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Warning!$\n$\nYour system has less than 8GB of memory (RAM).$\n$\n\
|
||||
You can still try to install Stable Diffusion UI,$\nbut it might have problems to start, or run$\nvery slowly."
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;Section -Post
|
||||
; WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\installer.exe"
|
||||
;SectionEnd
|
@ -1,15 +0,0 @@
|
||||
FROM python:3.9
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt update
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "old_port_main:app", "--host", "0.0.0.0", "--port", "8000"]
|
8
README BEFORE YOU RUN THIS.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Hi there,
|
||||
|
||||
What you have downloaded is meant for the developers of this project, not for users.
|
||||
|
||||
If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.
|
||||
Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation
|
||||
|
||||
Thanks
|
139
README.md
@ -1,82 +1,109 @@
|
||||
# Stable Diffusion UI
|
||||
### A simple way to install and use [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion) on your own computer
|
||||
### Easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer. No dependencies or technical knowledge required. 1-click install, powerful features.
|
||||
|
||||
---
|
||||
[](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](Troubleshooting.md)
|
||||
|
||||
🎉 **New!** `img2img` and `inpaint` (masking) are now supported! You can provide an image to generate new images based on it (and an optional text prompt). You can also use the generated image as the new input image in 1-click, to refine it further. (Thanks [Andreas](https://github.com/andreasjansson)!)
|
||||
New! Experimental support for Stable Diffusion 2.0 is available in beta!
|
||||
|
||||
# What does this do?
|
||||
Two things:
|
||||
1. Automatically downloads and installs Stable Diffusion on your own computer (no need to mess with conda or environments)
|
||||
2. Gives you a simple browser-based UI to talk to your local Stable Diffusion. Enter text prompts and view the generated image. No API keys required.
|
||||
----
|
||||
|
||||
All the processing will happen on your computer locally, it does not transmit your prompts or process on any remote server.
|
||||
## Step 1: Download the installer
|
||||
|
||||
<p float="left">
|
||||
<img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/shot-v3a.jpg" height="500" />
|
||||
<img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/shot-v6a.jpg" height="500" />
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-win.png" width="200" /></a>
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-linux.png" width="200" /></a>
|
||||
</p>
|
||||
|
||||
## Step 2: Run the program
|
||||
- On Windows: Double-click `Start Stable Diffusion UI.cmd`
|
||||
- On Linux: Run `./start.sh` in a terminal
|
||||
|
||||
## Step 3: There is no step 3!
|
||||
It's simple to get started. You don't need to install or struggle with Python, Anaconda, Docker etc.
|
||||
|
||||
The installer will take care of whatever is needed. A friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) will help you if you face any problems.
|
||||
|
||||
----
|
||||
|
||||
# Easy for new users, powerful features for advanced users
|
||||
### Features:
|
||||
- **No Dependencies or Technical Knowledge Required**: 1-click install for Windows 10/11 and Linux. *No dependencies*, no need for WSL or Docker or Conda or technical setup. Just download and run!
|
||||
- **Clutter-free UI**: a friendly and simple UI, while providing a lot of powerful features
|
||||
- Supports "*Text to Image*" and "*Image to Image*"
|
||||
- **Stable Diffusion 2.0 support (experimental)** - available in beta channel
|
||||
- **Custom Models**: Use your own `.ckpt` file, by placing it inside the `models/stable-diffusion` folder!
|
||||
- **Auto scan for malicious models** - uses picklescan to prevent malicious models
|
||||
- **Live Preview**: See the image as the AI is drawing it
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish
|
||||
- **In-Painting**: Specify areas of your image to paint into
|
||||
- **Face Correction (GFPGAN) and Upscaling (RealESRGAN)**
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **Loopback**: Use the output image as the input image for the next img2img task
|
||||
- **Negative Prompt**: Specify aspects of the image to *remove*.
|
||||
- **Attention/Emphasis:** () in the prompt increases the model's attention to enclosed words, and [] decreases it
|
||||
- **Weighted Prompts:** Use weights for specific words in your prompt to change their importance, e.g. `red:2.4 dragon:1.2`
|
||||
- **Prompt Matrix:** (in beta) Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`
|
||||
- **Lots of Samplers:** ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms
|
||||
- **Multiple Prompts File:** Queue multiple prompts by entering one prompt per line, or by running a text file
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*
|
||||
- **JPEG/PNG output**
|
||||
- **Save generated images to disk**
|
||||
- **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU.
|
||||
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
|
||||
- **Low Memory Usage**: Creates 512x512 images with less than 4GB of VRAM!
|
||||
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, and edit the conda environment.
|
||||
|
||||
### Easy for new users:
|
||||

|
||||
|
||||
### Powerful features for advanced users:
|
||||

|
||||
|
||||
### Live Preview
|
||||
Useful for judging (and stopping) an image quickly, without waiting for it to finish rendering.
|
||||
|
||||

|
||||
|
||||
### Task Queue
|
||||

|
||||
|
||||
# System Requirements
|
||||
1. Computer capable of running Stable Diffusion.
|
||||
2. Linux or Windows 11 (with [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)) or Windows 10 v2004+ (Build 19041+) with [WSL](https://docs.microsoft.com/en-us/windows/wsl/install).
|
||||
3. Requires (a) [Docker](https://docs.docker.com/engine/install/), (b) [docker-compose v1.29](https://docs.docker.com/compose/install/), and (c) [nvidia-container-toolkit](https://stackoverflow.com/a/58432877).
|
||||
1. Windows 10/11, or Linux. Experimental support for Mac is coming soon.
|
||||
2. An NVIDIA graphics card, preferably with 4GB or more of VRAM. If you don't have a compatible graphics card, it'll automatically run in the slower "CPU Mode".
|
||||
3. Minimum 8 GB of RAM and 25GB of disk space.
|
||||
|
||||
**Important:** If you're using Windows, please install docker inside your [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)'s Linux. Install docker for the Linux distro in your WSL. **Don't install Docker for Windows.**
|
||||
You don't need to install or struggle with Python, Anaconda, Docker etc. The installer will take care of whatever is needed.
|
||||
|
||||
# Installation
|
||||
1. Clone this repository: `git clone https://github.com/cmdr2/stable-diffusion-ui.git` or [download the zip file](https://github.com/cmdr2/stable-diffusion-ui/archive/refs/heads/main.zip) and unzip.
|
||||
2. Open your terminal, and in the project directory run: `docker-compose up &` (warning: this will take some time during the first run, since it'll download Stable Diffusion's [docker image](https://replicate.com/stability-ai/stable-diffusion), nearly 17 GiB)
|
||||
3. Open http://localhost:9000 in your browser. That's it!
|
||||
1. **Download** [for Windows](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.4.13/stable-diffusion-ui-windows.zip) or [for Linux](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.4.13/stable-diffusion-ui-linux.zip).
|
||||
|
||||
If you're getting errors, please check the [Troubleshooting](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) page.
|
||||
2. **Extract**:
|
||||
- For Windows: After unzipping the file, please move the `stable-diffusion-ui` folder to your `C:` (or any drive like D:, at the top root level), e.g. `C:\stable-diffusion-ui`. This will avoid a common problem with Windows (file path length limits).
|
||||
- For Linux: After extracting the .tar.xz file, please open a terminal, and go to the `stable-diffusion-ui` directory.
|
||||
|
||||
To stop the server, please run `docker-compose down`
|
||||
3. **Run**:
|
||||
- For Windows: `Start Stable Diffusion UI.cmd` by double-clicking it.
|
||||
- For Linux: In the terminal, run `./start.sh` (or `bash start.sh`)
|
||||
|
||||
# Usage
|
||||
Open http://localhost:9000 in your browser (after running `docker-compose up &` from step 2 previously).
|
||||
This will automatically install Stable Diffusion, set it up, and start the interface. No additional steps are needed.
|
||||
|
||||
## With a text description
|
||||
1. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||
2. Press `Make Image`. This will take some time, depending on your system's processing power.
|
||||
3. See the image generated using your prompt.
|
||||
**To Uninstall:** Just delete the `stable-diffusion-ui` folder to uninstall all the downloaded packages.
|
||||
|
||||
## With an image
|
||||
1. Click `Browse..` next to `Initial Image`. Select your desired image.
|
||||
2. An optional text prompt can help you further describe the kind of image you want to generate.
|
||||
3. Press `Make Image`. See the image generated using your prompt.
|
||||
|
||||
You can also set an `Image Mask` for telling Stable Diffusion to draw in only the black areas in your image mask. White areas in your mask will be ignored.
|
||||
|
||||
**Pro tip:** You can also click `Use as Input` on a generated image, to use it as the input image for your next generation. This can be useful for sequentially refining the generated image with a single click.
|
||||
|
||||
**Another tip:** Images with the same aspect ratio of your generated image work best. E.g. 1:1 if you're generating images sized 512x512.
|
||||
|
||||
## Problems?
|
||||
Please [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues) if this did not work for you (after trying the common [troubleshooting](#troubleshooting) steps)!
|
||||
|
||||
# Advanced Settings
|
||||
You can also set the configuration like `seed`, `width`, `height`, `num_outputs`, `num_inference_steps` and `guidance_scale` using the 'show' button next to 'Advanced settings'.
|
||||
|
||||
Use the same `seed` number to get the same image for a certain prompt. This is useful for refining a prompt without losing the basic image design. Enable the `random images` checkbox to get random images.
|
||||
|
||||

|
||||
|
||||
# Troubleshooting
|
||||
The [Troubleshooting wiki page](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) contains some common errors and their solutions. Please check that, and if it doesn't work, feel free to [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
# Behind the scenes
|
||||
This project is a quick way to get started with Stable Diffusion. You do not need to have Stable Diffusion already installed, and do not need any API keys. This project will automatically download Stable Diffusion's docker image, the first time it is run.
|
||||
|
||||
This project runs Stable Diffusion in a docker container behind the scenes, using Stable Diffusion's [Docker image](https://replicate.com/stability-ai/stable-diffusion) on replicate.com.
|
||||
# How to use?
|
||||
Please use our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use) to understand how to use the features in this UI.
|
||||
|
||||
# Bugs reports and code contributions welcome
|
||||
If there are any problems or suggestions, please feel free to [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
Also, please feel free to submit a pull request, if you have any code contributions in mind.
|
||||
We could really use help on these aspects (click to view tasks that need your help):
|
||||
* [User Interface](https://github.com/users/cmdr2/projects/1/views/1)
|
||||
* [Engine](https://github.com/users/cmdr2/projects/3/views/1)
|
||||
* [Installer](https://github.com/users/cmdr2/projects/4/views/1)
|
||||
* [Documentation](https://github.com/users/cmdr2/projects/5/views/1)
|
||||
|
||||
If you have any code contributions in mind, please feel free to say Hi to us on the [discord server](https://discord.com/invite/u9yhsFmEkB). We use the Discord server for development-related discussions, and for helping users.
|
||||
|
||||
# Disclaimer
|
||||
The authors of this project are not responsible for any content generated using this interface.
|
||||
|
||||
This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, spread misinformation and target vulnerable groups. For the full list of restrictions please read [the license](LICENSE).
|
||||
The license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, spread misinformation, or target vulnerable groups. For the full list of restrictions please read [the license](LICENSE). You agree to these terms by using this software.
|
||||
|
1
Troubleshooting.md
Normal file
@ -0,0 +1 @@
|
||||
Moved to https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
47
build.bat
Normal file
@ -0,0 +1,47 @@
|
||||
@echo off
|
||||
|
||||
@echo "Hi there, what you are running is meant for the developers of this project, not for users." & echo.
|
||||
@echo "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file."
|
||||
@echo "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation" & echo.
|
||||
@echo "If you are actually a developer of this project, please type Y and press enter" & echo.
|
||||
|
||||
set /p answer=Are you a developer of this project (Y/N)?
|
||||
if /i "%answer:~,1%" NEQ "Y" exit /b
|
||||
|
||||
mkdir dist\win\stable-diffusion-ui\scripts
|
||||
@REM mkdir dist\linux-mac\stable-diffusion-ui\scripts
|
||||
|
||||
@rem copy the installer files for Windows
|
||||
|
||||
copy scripts\on_env_start.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy scripts\bootstrap.bat dist\win\stable-diffusion-ui\scripts\
|
||||
copy "scripts\Start Stable Diffusion UI.cmd" dist\win\stable-diffusion-ui\
|
||||
copy LICENSE dist\win\stable-diffusion-ui\
|
||||
copy "CreativeML Open RAIL-M License" dist\win\stable-diffusion-ui\
|
||||
copy "How to install and run.txt" dist\win\stable-diffusion-ui\
|
||||
echo. > dist\win\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem copy the installer files for Linux and Mac
|
||||
|
||||
@REM copy scripts\on_env_start.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\bootstrap.sh dist\linux-mac\stable-diffusion-ui\scripts\
|
||||
@REM copy scripts\start.sh dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy LICENSE dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "CreativeML Open RAIL-M License" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM copy "How to install and run.txt" dist\linux-mac\stable-diffusion-ui\
|
||||
@REM echo. > dist\linux-mac\stable-diffusion-ui\scripts\install_status.txt
|
||||
|
||||
@rem make the zip
|
||||
|
||||
cd dist\win
|
||||
call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-windows.zip
|
||||
cd ..\..
|
||||
|
||||
@REM cd dist\linux-mac
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-linux.zip
|
||||
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-mac.zip
|
||||
@REM cd ..\..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
||||
|
||||
pause
|
49
build.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
printf "Hi there, what you are running is meant for the developers of this project, not for users.\n\n"
|
||||
printf "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.\n"
|
||||
printf "Please download and follow the instructions at https://github.com/cmdr2/stable-diffusion-ui#installation\n\n"
|
||||
printf "If you are actually a developer of this project, please type Y and press enter\n\n"
|
||||
|
||||
read -p "Are you a developer of this project (Y/N) " yn
|
||||
case $yn in
|
||||
[Yy]* ) ;;
|
||||
* ) exit;;
|
||||
esac
|
||||
|
||||
# mkdir -p dist/win/stable-diffusion-ui/scripts
|
||||
mkdir -p dist/linux-mac/stable-diffusion-ui/scripts
|
||||
|
||||
# copy the installer files for Windows
|
||||
|
||||
# cp scripts/on_env_start.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp scripts/bootstrap.bat dist/win/stable-diffusion-ui/scripts/
|
||||
# cp "scripts/Start Stable Diffusion UI.cmd" dist/win/stable-diffusion-ui/
|
||||
# cp LICENSE dist/win/stable-diffusion-ui/
|
||||
# cp "CreativeML Open RAIL-M License" dist/win/stable-diffusion-ui/
|
||||
# cp "How to install and run.txt" dist/win/stable-diffusion-ui/
|
||||
# echo "" > dist/win/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# copy the installer files for Linux and Mac
|
||||
|
||||
cp scripts/on_env_start.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/bootstrap.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/functions.sh dist/linux-mac/stable-diffusion-ui/scripts/
|
||||
cp scripts/start.sh dist/linux-mac/stable-diffusion-ui/
|
||||
cp LICENSE dist/linux-mac/stable-diffusion-ui/
|
||||
cp "CreativeML Open RAIL-M License" dist/linux-mac/stable-diffusion-ui/
|
||||
cp "How to install and run.txt" dist/linux-mac/stable-diffusion-ui/
|
||||
echo "" > dist/linux-mac/stable-diffusion-ui/scripts/install_status.txt
|
||||
|
||||
# make the zip
|
||||
|
||||
# cd dist/win
|
||||
# zip -r ../stable-diffusion-ui-windows.zip stable-diffusion-ui
|
||||
# cd ../..
|
||||
|
||||
cd dist/linux-mac
|
||||
zip -r ../stable-diffusion-ui-linux.zip stable-diffusion-ui
|
||||
zip -r ../stable-diffusion-ui-mac.zip stable-diffusion-ui
|
||||
cd ../..
|
||||
|
||||
echo "Build ready. Upload the zip files inside the 'dist' folder."
|
@ -1,38 +0,0 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
stability-ai:
|
||||
container_name: sd
|
||||
ports:
|
||||
- '5000:5000'
|
||||
image: 'r8.im/stability-ai/stable-diffusion@sha256:be04660a5b93ef2aff61e3668dedb4cbeb14941e62a3fd5998364a32d613e35e'
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
|
||||
stable-diffusion-ui:
|
||||
container_name: sd-ui
|
||||
ports:
|
||||
- '9000:9000'
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- .:/app
|
||||
depends_on:
|
||||
- stability-ai
|
||||
|
||||
stable-diffusion-old-port-redirect:
|
||||
container_name: sd-old-port-redirect
|
||||
ports:
|
||||
- '8000:8000'
|
||||
build:
|
||||
context: .
|
||||
dockerfile: OldPortDockerfile
|
||||
volumes:
|
||||
- .:/app
|
||||
|
||||
networks:
|
||||
default:
|
7
environment.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
name: stable-diffusion-ui-installer
|
||||
channels:
|
||||
- defaults
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- conda
|
||||
- git
|
554
index.html
@ -1,554 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
a {
|
||||
color: rgb(0, 102, 204);
|
||||
}
|
||||
a:visited {
|
||||
color: rgb(0, 102, 204);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
label {
|
||||
font-size: 10pt;
|
||||
}
|
||||
#prompt {
|
||||
width: 50vw;
|
||||
height: 50pt;
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
#prompt {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
.image_preview_container {
|
||||
display: none;
|
||||
}
|
||||
.image_clear_btn {
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
background: black;
|
||||
color: white;
|
||||
border: 2pt solid #ccc;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
border-radius: 8pt;
|
||||
width: 16pt;
|
||||
height: 16pt;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#configHeader {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#config {
|
||||
font-size: 9pt;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
#outputMsg {
|
||||
font-size: small;
|
||||
}
|
||||
#footer {
|
||||
border-top: 1px solid #999;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
font-size: small;
|
||||
}
|
||||
.imgSeedLabel {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 5pt;
|
||||
margin-left: -5pt;
|
||||
font-size: 10pt;
|
||||
|
||||
background-color: #333;
|
||||
opacity: 0.8;
|
||||
color: #ddd;
|
||||
border-radius: 3pt;
|
||||
padding: 1pt 3pt;
|
||||
}
|
||||
.imgUseBtn {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 30pt;
|
||||
margin-left: -5pt;
|
||||
}
|
||||
.imgSaveBtn {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 55pt;
|
||||
margin-left: -5pt;
|
||||
}
|
||||
.imgItem {
|
||||
display: inline;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.imgItemInfo {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
<body>
|
||||
<div id="status">Server status: <span id="serverStatus">checking..</span> | Request status: <span id="reqStatus">n/a</span></div>
|
||||
|
||||
<br/>
|
||||
|
||||
<b>Prompt:</b><br/>
|
||||
<textarea id="prompt">a photograph of an astronaut riding a horse</textarea><br/>
|
||||
|
||||
<label for="init_image"><b>Initial Image:</b> (optional) </label> <input id="init_image" name="init_image" type="file" /> </button><br/>
|
||||
<div id="init_image_preview_container" class="image_preview_container">
|
||||
<img id="init_image_preview" src="" width="100" height="100" />
|
||||
<button id="init_image_clear" class="image_clear_btn">X</button>
|
||||
</div><br/>
|
||||
|
||||
<div id="mask_setting">
|
||||
<label for="mask"><b>Image Mask:</b> (optional) </label> <input id="mask" name="mask" type="file" /> </button><br/>
|
||||
<div id="mask_preview_container" class="image_preview_container">
|
||||
<img id="mask_preview" src="" width="100" height="100" />
|
||||
<button id="mask_clear" class="image_clear_btn">X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="configHeader"><b>Advanced settings:</b> [<a id="configToggleBtn" href="#">show</a>]</div>
|
||||
<div id="config">
|
||||
<label for="seed">Seed:</label> <input id="seed" name="seed" size="10" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked> <label for="random_seed">Random Image</label> <br/>
|
||||
<label for="num_outputs_total">Number of outputs:</label> <input id="num_outputs_total" name="num_outputs_total" value="1" size="4"> <label for="num_outputs_parallel">Generate in parallel:</label> <select id="num_outputs_parallel" name="num_outputs_parallel" value="1"><option value="1" selected>1 image at a time</option><option value="4">4 images at a time</option></select><br/>
|
||||
<label for="width">Width:</label> <select id="width" name="width" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option><option value="1024">1024</option></select><br/>
|
||||
<label for="height">Height:</label> <select id="height" name="height" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option></select><br/>
|
||||
<label for="num_inference_steps">Number of inference steps:</label> <input id="num_inference_steps" name="num_inference_steps" size="4" value="50"><br/>
|
||||
<label for="guidance_scale">Guidance Scale:</label> <input id="guidance_scale" name="guidance_scale" value="75" type="range" min="10" max="200"> <span id="guidance_scale_value"></span><br/>
|
||||
<span id="prompt_strength_container"><label for="prompt_strength">Prompt Strength:</label> <input id="prompt_strength" name="prompt_strength" value="8" type="range" min="0" max="10"> <span id="prompt_strength_value"></span><br/></span><br/>
|
||||
<input id="sound_toggle" name="sound_toggle" type="checkbox" checked> <label for="sound_toggle">Play sound on task completion</label><br/>
|
||||
</div>
|
||||
|
||||
<button id="makeImage">Make Image</button> <br/><br/>
|
||||
|
||||
<div id="outputMsg"></div>
|
||||
|
||||
<div id="images"></div>
|
||||
|
||||
<div id="footer">
|
||||
<p>Please feel free to <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const SOUND_ENABLED_KEY = "soundEnabled"
|
||||
const HEALTH_PING_INTERVAL = 5 // seconds
|
||||
|
||||
let promptField = document.querySelector('#prompt')
|
||||
let numOutputsTotalField = document.querySelector('#num_outputs_total')
|
||||
let numOutputsParallelField = document.querySelector('#num_outputs_parallel')
|
||||
let numInferenceStepsField = document.querySelector('#num_inference_steps')
|
||||
let guidanceScaleField = document.querySelector('#guidance_scale')
|
||||
let guidanceScaleValueLabel = document.querySelector('#guidance_scale_value')
|
||||
let randomSeedField = document.querySelector("#random_seed")
|
||||
let seedField = document.querySelector('#seed')
|
||||
let widthField = document.querySelector('#width')
|
||||
let heightField = document.querySelector('#height')
|
||||
let initImageSelector = document.querySelector("#init_image")
|
||||
let initImagePreview = document.querySelector("#init_image_preview")
|
||||
let maskImageSelector = document.querySelector("#mask")
|
||||
let maskImagePreview = document.querySelector("#mask_preview")
|
||||
let promptStrengthField = document.querySelector('#prompt_strength')
|
||||
let promptStrengthValueLabel = document.querySelector('#prompt_strength_value')
|
||||
|
||||
let makeImageBtn = document.querySelector('#makeImage')
|
||||
|
||||
let imagesContainer = document.querySelector('#images')
|
||||
let initImagePreviewContainer = document.querySelector('#init_image_preview_container')
|
||||
let initImageClearBtn = document.querySelector('#init_image_clear')
|
||||
let promptStrengthContainer = document.querySelector('#prompt_strength_container')
|
||||
|
||||
let maskSetting = document.querySelector('#mask_setting')
|
||||
let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
|
||||
let maskImageClearBtn = document.querySelector('#mask_clear')
|
||||
|
||||
let showConfigToggle = document.querySelector('#configToggleBtn')
|
||||
let configBox = document.querySelector('#config')
|
||||
let outputMsg = document.querySelector('#outputMsg')
|
||||
|
||||
let soundToggle = document.querySelector('#sound_toggle')
|
||||
|
||||
let serverStatus = 'offline'
|
||||
|
||||
function isSoundEnabled() {
|
||||
if (localStorage.getItem(SOUND_ENABLED_KEY) === 'false') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function setStatus(statusType, msg, msgType) {
|
||||
let el = ''
|
||||
|
||||
if (statusType === 'server') {
|
||||
el = '#serverStatus'
|
||||
serverStatus = msg
|
||||
} else if (statusType === 'request') {
|
||||
el = '#reqStatus'
|
||||
}
|
||||
|
||||
if (msgType == 'error') {
|
||||
msg = '<span style="color: red">' + msg + '<span>'
|
||||
} else if (msgType == 'success') {
|
||||
msg = '<span style="color: green">' + msg + '<span>'
|
||||
}
|
||||
|
||||
if (el) {
|
||||
document.querySelector(el).innerHTML = msg
|
||||
}
|
||||
}
|
||||
|
||||
function logError(msg, res) {
|
||||
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
|
||||
console.log('request error', res)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
function playSound() {
|
||||
const audio = new Audio('/media/ding.mp3')
|
||||
audio.volume = 0.2
|
||||
audio.play()
|
||||
}
|
||||
|
||||
async function healthCheck() {
|
||||
try {
|
||||
let res = await fetch('/ping')
|
||||
res = await res.json()
|
||||
|
||||
if (res[0] == 'OK') {
|
||||
setStatus('server', 'online', 'success')
|
||||
} else {
|
||||
setStatus('server', 'offline', 'error')
|
||||
}
|
||||
} catch (e) {
|
||||
setStatus('server', 'offline', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// makes a single image. don't call this directly, use makeImage() instead
|
||||
async function doMakeImage(reqBody) {
|
||||
let res = ''
|
||||
let seed = reqBody['seed']
|
||||
|
||||
try {
|
||||
res = await fetch('/image', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqBody)
|
||||
})
|
||||
|
||||
if (res.status != 200) {
|
||||
if (serverStatus === 'online') {
|
||||
logError('Stable Diffusion had an error: ' + await res.text() + '. This happens sometimes. Maybe modify the prompt or seed a little bit?', res)
|
||||
} else {
|
||||
logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed.", res)
|
||||
}
|
||||
res = undefined
|
||||
} else {
|
||||
res = await res.json()
|
||||
|
||||
if (res.status !== 'succeeded') {
|
||||
let msg = ''
|
||||
if (res.detail !== undefined) {
|
||||
msg = res.detail[0].msg + " in " + JSON.stringify(res.detail[0].loc)
|
||||
} else {
|
||||
msg = res
|
||||
}
|
||||
logError(msg, res)
|
||||
res = undefined
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('request error', e)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let idx in res.output) {
|
||||
let imgBody = ''
|
||||
|
||||
try {
|
||||
imgBody = res.output[idx]
|
||||
} catch (e) {
|
||||
console.log(imgBody)
|
||||
setStatus('request', 'invalid image', 'error')
|
||||
continue
|
||||
}
|
||||
|
||||
let imgItem = document.createElement('div')
|
||||
imgItem.className = 'imgItem'
|
||||
|
||||
let img = document.createElement('img')
|
||||
img.width = parseInt(reqBody.width)
|
||||
img.height = parseInt(reqBody.height)
|
||||
img.src = imgBody
|
||||
|
||||
let imgItemInfo = document.createElement('span')
|
||||
imgItemInfo.className = 'imgItemInfo'
|
||||
|
||||
let imgSeedLabel = document.createElement('span')
|
||||
imgSeedLabel.className = 'imgSeedLabel'
|
||||
imgSeedLabel.innerHTML = 'Seed: ' + seed
|
||||
|
||||
let imgUseBtn = document.createElement('button')
|
||||
imgUseBtn.className = 'imgUseBtn'
|
||||
imgUseBtn.innerHTML = 'Use as Input'
|
||||
|
||||
let imgSaveBtn = document.createElement('button')
|
||||
imgSaveBtn.className = 'imgSaveBtn'
|
||||
imgSaveBtn.innerHTML = 'Download'
|
||||
|
||||
imgItem.appendChild(img)
|
||||
imgItem.appendChild(imgItemInfo)
|
||||
imgItemInfo.appendChild(imgSeedLabel)
|
||||
imgItemInfo.appendChild(imgUseBtn)
|
||||
imgItemInfo.appendChild(imgSaveBtn)
|
||||
imagesContainer.appendChild(imgItem)
|
||||
|
||||
imgUseBtn.addEventListener('click', function() {
|
||||
initImageSelector.value = null
|
||||
initImagePreview.src = imgBody
|
||||
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
|
||||
randomSeedField.checked = false
|
||||
seedField.value = seed
|
||||
seedField.disabled = false
|
||||
})
|
||||
|
||||
imgSaveBtn.addEventListener('click', function() {
|
||||
let imgDownload = document.createElement('a')
|
||||
imgDownload.download = generateUUID() + '.png'
|
||||
imgDownload.href = imgBody
|
||||
imgDownload.click()
|
||||
})
|
||||
|
||||
imgItem.addEventListener('mouseenter', function() {
|
||||
imgItemInfo.style.opacity = 1
|
||||
})
|
||||
|
||||
imgItem.addEventListener('mouseleave', function() {
|
||||
imgItemInfo.style.opacity = 0.5
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function makeImage() {
|
||||
setStatus('request', 'fetching..')
|
||||
|
||||
makeImageBtn.innerHTML = 'Processing..'
|
||||
makeImageBtn.disabled = true
|
||||
|
||||
outputMsg.innerHTML = 'Fetching..'
|
||||
|
||||
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000) : parseInt(seedField.value))
|
||||
let numOutputsTotal = parseInt(numOutputsTotalField.value)
|
||||
let numOutputsParallel = parseInt(numOutputsParallelField.value)
|
||||
let batchCount = Math.ceil(numOutputsTotal / numOutputsParallel)
|
||||
let batchSize = numOutputsParallel
|
||||
|
||||
let reqBody = {
|
||||
prompt: promptField.value,
|
||||
num_outputs: batchSize,
|
||||
num_inference_steps: numInferenceStepsField.value,
|
||||
guidance_scale: parseInt(guidanceScaleField.value) / 10,
|
||||
width: widthField.value,
|
||||
height: heightField.value,
|
||||
}
|
||||
|
||||
if (initImagePreview.src.indexOf('data:image/png;base64') !== -1) {
|
||||
reqBody['init_image'] = initImagePreview.src
|
||||
reqBody['prompt_strength'] = parseInt(promptStrengthField.value) / 10
|
||||
|
||||
if (maskImagePreview.src.indexOf('data:image/png;base64') !== -1) {
|
||||
reqBody['mask'] = maskImagePreview.src
|
||||
}
|
||||
}
|
||||
|
||||
let time = new Date().getTime()
|
||||
imagesContainer.innerHTML = ''
|
||||
|
||||
for (let i = 0; i < batchCount; i++) {
|
||||
reqBody['seed'] = seed + i
|
||||
|
||||
await doMakeImage(reqBody)
|
||||
|
||||
outputMsg.innerHTML = 'Processed batch ' + (i+1) + '/' + batchCount
|
||||
}
|
||||
|
||||
makeImageBtn.innerHTML = 'Make Image'
|
||||
makeImageBtn.disabled = false
|
||||
|
||||
if (isSoundEnabled()) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
time = new Date().getTime() - time
|
||||
time /= 1000
|
||||
|
||||
outputMsg.innerHTML = 'Processed ' + numOutputsTotal + ' images in ' + time + ' seconds'
|
||||
|
||||
setStatus('request', 'done', 'success')
|
||||
|
||||
if (randomSeedField.checked) {
|
||||
seedField.value = seed
|
||||
}
|
||||
}
|
||||
|
||||
function generateUUID() { // Public Domain/MIT
|
||||
var d = new Date().getTime();//Timestamp
|
||||
var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16;//random number between 0 and 16
|
||||
if(d > 0){//Use timestamp until depleted
|
||||
r = (d + r)%16 | 0;
|
||||
d = Math.floor(d/16);
|
||||
} else {//Use microseconds since page-load if supported
|
||||
r = (d2 + r)%16 | 0;
|
||||
d2 = Math.floor(d2/16);
|
||||
}
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function handleAudioEnabledChange(e) {
|
||||
localStorage.setItem(SOUND_ENABLED_KEY, e.target.checked.toString())
|
||||
}
|
||||
|
||||
soundToggle.addEventListener('click', handleAudioEnabledChange)
|
||||
soundToggle.checked = isSoundEnabled();
|
||||
|
||||
makeImageBtn.addEventListener('click', makeImage)
|
||||
|
||||
configBox.style.display = 'none'
|
||||
|
||||
showConfigToggle.addEventListener('click', function() {
|
||||
configBox.style.display = (configBox.style.display === 'none' ? 'block' : 'none')
|
||||
showConfigToggle.innerHTML = (configBox.style.display === 'none' ? 'show' : 'hide')
|
||||
return false
|
||||
})
|
||||
|
||||
function updateGuidanceScale() {
|
||||
guidanceScaleValueLabel.innerHTML = guidanceScaleField.value / 10
|
||||
}
|
||||
|
||||
guidanceScaleField.addEventListener('input', updateGuidanceScale)
|
||||
updateGuidanceScale()
|
||||
|
||||
function updatePromptStrength() {
|
||||
promptStrengthValueLabel.innerHTML = promptStrengthField.value / 10
|
||||
}
|
||||
|
||||
promptStrengthField.addEventListener('input', updatePromptStrength)
|
||||
updatePromptStrength()
|
||||
|
||||
function checkRandomSeed() {
|
||||
if (randomSeedField.checked) {
|
||||
seedField.disabled = true
|
||||
seedField.value = "random"
|
||||
} else {
|
||||
seedField.disabled = false
|
||||
}
|
||||
}
|
||||
randomSeedField.addEventListener('input', checkRandomSeed)
|
||||
checkRandomSeed()
|
||||
|
||||
function showInitImagePreview() {
|
||||
if (initImageSelector.files.length === 0) {
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
maskSetting.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
let file = initImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
// console.log(file.name, reader.result)
|
||||
initImagePreview.src = reader.result
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
initImageSelector.addEventListener('change', showInitImagePreview)
|
||||
showInitImagePreview()
|
||||
|
||||
initImageClearBtn.addEventListener('click', function() {
|
||||
initImageSelector.value = null
|
||||
maskImageSelector.value = null
|
||||
|
||||
initImagePreview.src = ''
|
||||
maskImagePreview.src = ''
|
||||
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
|
||||
maskSetting.style.display = 'none'
|
||||
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
})
|
||||
|
||||
function showMaskImagePreview() {
|
||||
if (maskImageSelector.files.length === 0) {
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
let file = maskImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
maskImagePreview.src = reader.result
|
||||
maskImagePreviewContainer.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
maskImageSelector.addEventListener('change', showMaskImagePreview)
|
||||
showMaskImagePreview()
|
||||
|
||||
maskImageClearBtn.addEventListener('click', function() {
|
||||
maskImageSelector.value = null
|
||||
maskImagePreview.src = ''
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
})
|
||||
|
||||
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||
</script>
|
||||
|
||||
</html>
|
69
main.py
@ -1,69 +0,0 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from starlette.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
import requests
|
||||
|
||||
LOCAL_SERVER_URL = 'http://stability-ai:5000'
|
||||
PREDICT_URL = LOCAL_SERVER_URL + '/predictions'
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# defaults from https://huggingface.co/blog/stable_diffusion
|
||||
class ImageRequest(BaseModel):
|
||||
prompt: str
|
||||
init_image: str = None # base64
|
||||
mask: str = None # base64
|
||||
num_outputs: str = "1"
|
||||
num_inference_steps: str = "50"
|
||||
guidance_scale: str = "7.5"
|
||||
width: str = "512"
|
||||
height: str = "512"
|
||||
seed: str = "30000"
|
||||
prompt_strength: str = "0.8"
|
||||
|
||||
@app.get('/')
|
||||
def read_root():
|
||||
return FileResponse('index.html')
|
||||
|
||||
@app.get('/ping')
|
||||
async def ping():
|
||||
try:
|
||||
requests.get(LOCAL_SERVER_URL)
|
||||
return {'OK'}
|
||||
except:
|
||||
return {'ERROR'}
|
||||
|
||||
@app.post('/image')
|
||||
async def image(req : ImageRequest):
|
||||
data = {
|
||||
"input": {
|
||||
"prompt": req.prompt,
|
||||
"num_outputs": req.num_outputs,
|
||||
"num_inference_steps": req.num_inference_steps,
|
||||
"width": req.width,
|
||||
"height": req.height,
|
||||
"seed": req.seed,
|
||||
"guidance_scale": req.guidance_scale,
|
||||
}
|
||||
}
|
||||
|
||||
if req.init_image is not None:
|
||||
data['input']['init_image'] = req.init_image
|
||||
data['input']['prompt_strength'] = req.prompt_strength
|
||||
|
||||
if req.mask is not None:
|
||||
data['input']['mask'] = req.mask
|
||||
|
||||
if req.seed == "-1":
|
||||
del data['input']['seed']
|
||||
|
||||
res = requests.post(PREDICT_URL, json=data)
|
||||
if res.status_code != 200:
|
||||
raise HTTPException(status_code=500, detail=res.text)
|
||||
|
||||
return res.json()
|
||||
|
||||
@app.get('/media/ding.mp3')
|
||||
def read_root():
|
||||
return FileResponse('media/ding.mp3')
|
BIN
media/config-v4.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
media/config-v5.jpg
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
media/config-v6.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
media/config-v6.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
media/config-v7.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
media/download buttons.xcf
Normal file
BIN
media/download-linux.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
media/download-win.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
media/modifiers-v1.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
media/shot-v10-simple.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
media/shot-v10.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
media/shot-v8.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
BIN
media/shot-v9.jpg
Normal file
After Width: | Height: | Size: 199 KiB |
BIN
media/system-settings-v2.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
media/task-queue-v1.jpg
Normal file
After Width: | Height: | Size: 155 KiB |
@ -1,36 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/', response_class=HTMLResponse)
|
||||
def read_root():
|
||||
return '''
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial;
|
||||
font-size: 11pt;
|
||||
}
|
||||
pre {
|
||||
display: inline;
|
||||
background: #aaa;
|
||||
padding: 2px;
|
||||
border: 1px solid #777;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
pre {
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h4>The UI has moved to <a href="http://localhost:9000">http://localhost:9000</a>. The current address that you used (ending with :8000) will be removed in the future, so please use <a href="http://localhost:9000">http://localhost:9000</a> going ahead (and in any bookmarks you've saved).</h4>
|
||||
|
||||
<h3>Why has the address changed?</h3>
|
||||
<p>The previously used port (8000) is often used by other servers, which results in port conflicts. So the project's port number has been changed, while the project is still young. Otherwise port-conflicts with 8000 will be a common source of new-user issues in the future.</p>
|
||||
<p>Sorry about this, and apologies for the inconvenience :)</p>
|
||||
'''
|
@ -1,3 +0,0 @@
|
||||
requests
|
||||
fastapi==0.80.0
|
||||
uvicorn==0.18.2
|
46
scripts/Developer Console.cmd
Normal file
@ -0,0 +1,46 @@
|
||||
@echo off
|
||||
|
||||
echo "Opening Stable Diffusion UI - Developer Console.." & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@rem set legacy and new installer's PATH, if they exist
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
|
||||
@rem Test the environment
|
||||
echo "Environment Info:"
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
|
||||
echo.
|
||||
|
||||
@rem activate the environment
|
||||
call conda activate .\stable-diffusion\env
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
@rem set the PYTHONPATH
|
||||
cd stable-diffusion
|
||||
set SD_DIR=%cd%
|
||||
|
||||
cd env\lib\site-packages
|
||||
set PYTHONPATH=%SD_DIR%;%cd%
|
||||
cd ..\..\..
|
||||
echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
cd ..
|
||||
|
||||
@rem done
|
||||
echo.
|
||||
|
||||
cmd /k
|
27
scripts/Start Stable Diffusion UI.cmd
Normal file
@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
|
||||
cd /d %~dp0
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@rem set legacy installer's PATH, if it exists
|
||||
if exist "installer" set PATH=%cd%\installer;%cd%\installer\Library\bin;%cd%\installer\Scripts;%cd%\installer\Library\usr\bin;%PATH%
|
||||
|
||||
@rem Setup the packages required for the installer
|
||||
call scripts\bootstrap.bat
|
||||
|
||||
@rem set new installer's PATH, if it downloaded any packages
|
||||
if exist "installer_files\env" set PATH=%cd%\installer_files\env;%cd%\installer_files\env\Library\bin;%cd%\installer_files\env\Scripts;%cd%\installer_files\Library\usr\bin;%PATH%
|
||||
|
||||
set PYTHONPATH=%cd%\installer;%cd%\installer_files\env
|
||||
|
||||
@rem Test the bootstrap
|
||||
call where git
|
||||
call git --version
|
||||
|
||||
call where conda
|
||||
call conda --version
|
||||
|
||||
@rem Download the rest of the installer and UI
|
||||
call scripts\on_env_start.bat
|
||||
|
||||
@pause
|
77
scripts/bootstrap.bat
Normal file
@ -0,0 +1,77 @@
|
||||
@echo off
|
||||
|
||||
@rem This script will install git and conda (if not found on the PATH variable)
|
||||
@rem using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
@rem For users who already have git and conda, this step will be skipped.
|
||||
|
||||
@rem This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
@rem config
|
||||
set MAMBA_ROOT_PREFIX=%cd%\installer_files\mamba
|
||||
set INSTALL_ENV_DIR=%cd%\installer_files\env
|
||||
set LEGACY_INSTALL_ENV_DIR=%cd%\installer
|
||||
set MICROMAMBA_DOWNLOAD_URL=https://github.com/cmdr2/stable-diffusion-ui/releases/download/v1.1/micromamba.exe
|
||||
set umamba_exists=F
|
||||
|
||||
set OLD_APPDATA=%APPDATA%
|
||||
set OLD_USERPROFILE=%USERPROFILE%
|
||||
set APPDATA=%cd%\installer_files\appdata
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
@rem figure out whether git and conda needs to be installed
|
||||
if exist "%INSTALL_ENV_DIR%" set PATH=%INSTALL_ENV_DIR%;%INSTALL_ENV_DIR%\Library\bin;%INSTALL_ENV_DIR%\Scripts;%INSTALL_ENV_DIR%\Library\usr\bin;%PATH%
|
||||
|
||||
set PACKAGES_TO_INSTALL=
|
||||
|
||||
if not exist "%LEGACY_INSTALL_ENV_DIR%\etc\profile.d\conda.sh" (
|
||||
if not exist "%INSTALL_ENV_DIR%\etc\profile.d\conda.sh" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% conda
|
||||
)
|
||||
|
||||
call git --version >.tmp1 2>.tmp2
|
||||
if "%ERRORLEVEL%" NEQ "0" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% git
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version >.tmp1 2>.tmp2
|
||||
if "%ERRORLEVEL%" EQU "0" set umamba_exists=T
|
||||
|
||||
@rem (if necessary) install git and conda into a contained environment
|
||||
if "%PACKAGES_TO_INSTALL%" NEQ "" (
|
||||
@rem download micromamba
|
||||
if "%umamba_exists%" == "F" (
|
||||
echo "Downloading micromamba from %MICROMAMBA_DOWNLOAD_URL% to %MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
mkdir "%MAMBA_ROOT_PREFIX%"
|
||||
call curl -Lk "%MICROMAMBA_DOWNLOAD_URL%" > "%MAMBA_ROOT_PREFIX%\micromamba.exe"
|
||||
|
||||
if "%ERRORLEVEL%" NEQ "0" (
|
||||
echo "There was a problem downloading micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
mkdir "%APPDATA%"
|
||||
mkdir "%USERPROFILE%"
|
||||
|
||||
@rem test the mamba binary
|
||||
echo Micromamba version:
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version
|
||||
)
|
||||
|
||||
@rem create the installer env
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" create -y --prefix "%INSTALL_ENV_DIR%"
|
||||
)
|
||||
|
||||
echo "Packages to install:%PACKAGES_TO_INSTALL%"
|
||||
|
||||
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" install -y --prefix "%INSTALL_ENV_DIR%" -c conda-forge %PACKAGES_TO_INSTALL%
|
||||
|
||||
if not exist "%INSTALL_ENV_DIR%" (
|
||||
echo "There was a problem while installing%PACKAGES_TO_INSTALL% using micromamba. Cannot continue."
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@rem revert to the old APPDATA. only needed it for bypassing a bug in micromamba (with special characters)
|
||||
set APPDATA=%OLD_APPDATA%
|
||||
set USERPROFILE=%OLD_USERPROFILE%
|
86
scripts/bootstrap.sh
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will install git and conda (if not found on the PATH variable)
|
||||
# using micromamba (an 8mb static-linked single-file binary, conda replacement).
|
||||
# For users who already have git and conda, this step will be skipped.
|
||||
|
||||
# This enables a user to install this project without manually installing conda and git.
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
set -o pipefail
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
case "${OS_NAME}" in
|
||||
Linux*) OS_NAME="linux";;
|
||||
Darwin*) OS_NAME="osx";;
|
||||
*) echo "Unknown OS: $OS_NAME! This script runs only on Linux or Mac" && exit
|
||||
esac
|
||||
|
||||
OS_ARCH=$(uname -m)
|
||||
case "${OS_ARCH}" in
|
||||
x86_64*) OS_ARCH="64";;
|
||||
arm64*) OS_ARCH="arm64";;
|
||||
*) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit
|
||||
esac
|
||||
|
||||
# https://mamba.readthedocs.io/en/latest/installation.html
|
||||
if [ "$OS_NAME" == "linux" ] && [ "$OS_ARCH" == "arm64" ]; then OS_ARCH="aarch64"; fi
|
||||
|
||||
# config
|
||||
export MAMBA_ROOT_PREFIX="$(pwd)/installer_files/mamba"
|
||||
INSTALL_ENV_DIR="$(pwd)/installer_files/env"
|
||||
LEGACY_INSTALL_ENV_DIR="$(pwd)/installer"
|
||||
MICROMAMBA_DOWNLOAD_URL="https://micro.mamba.pm/api/micromamba/${OS_NAME}-${OS_ARCH}/latest"
|
||||
umamba_exists="F"
|
||||
|
||||
# figure out whether git and conda needs to be installed
|
||||
if [ -e "$INSTALL_ENV_DIR" ]; then export PATH="$INSTALL_ENV_DIR/bin:$PATH"; fi
|
||||
|
||||
PACKAGES_TO_INSTALL=""
|
||||
|
||||
if [ ! -e "$LEGACY_INSTALL_ENV_DIR/etc/profile.d/conda.sh" ] && [ ! -e "$INSTALL_ENV_DIR/etc/profile.d/conda.sh" ]; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL conda"; fi
|
||||
if ! hash "git" &>/dev/null; then PACKAGES_TO_INSTALL="$PACKAGES_TO_INSTALL git"; fi
|
||||
|
||||
if "$MAMBA_ROOT_PREFIX/micromamba" --version &>/dev/null; then umamba_exists="T"; fi
|
||||
|
||||
# (if necessary) install git and conda into a contained environment
|
||||
if [ "$PACKAGES_TO_INSTALL" != "" ]; then
|
||||
# download micromamba
|
||||
if [ "$umamba_exists" == "F" ]; then
|
||||
echo "Downloading micromamba from $MICROMAMBA_DOWNLOAD_URL to $MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
mkdir -p "$MAMBA_ROOT_PREFIX"
|
||||
curl -L "$MICROMAMBA_DOWNLOAD_URL" | tar -xvj bin/micromamba -O > "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo
|
||||
echo "EE micromamba download failed"
|
||||
echo "EE If the lines above contain 'bzip2: Cannot exec', your system doesn't have bzip2 installed"
|
||||
echo "EE If there are network errors, please check your internet setup"
|
||||
fail "micromamba download failed"
|
||||
fi
|
||||
|
||||
chmod u+x "$MAMBA_ROOT_PREFIX/micromamba"
|
||||
|
||||
# test the mamba binary
|
||||
echo "Micromamba version:"
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" --version
|
||||
fi
|
||||
|
||||
# create the installer env
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" create -y --prefix "$INSTALL_ENV_DIR" || fail "unable to create the install environment"
|
||||
fi
|
||||
|
||||
if [ ! -e "$INSTALL_ENV_DIR" ]; then
|
||||
fail "There was a problem while installing$PACKAGES_TO_INSTALL using micromamba. Cannot continue."
|
||||
fi
|
||||
|
||||
echo "Packages to install:$PACKAGES_TO_INSTALL"
|
||||
|
||||
"$MAMBA_ROOT_PREFIX/micromamba" install -y --prefix "$INSTALL_ENV_DIR" -c conda-forge $PACKAGES_TO_INSTALL
|
||||
if [ "$?" != "0" ]; then
|
||||
fail "Installation of the packages '$PACKAGES_TO_INSTALL' failed."
|
||||
fi
|
||||
fi
|
51
scripts/developer_console.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ "$0" == "bash" ]; then
|
||||
echo "Opening Stable Diffusion UI - Developer Console.."
|
||||
echo ""
|
||||
|
||||
# set legacy and new installer's PATH, if they exist
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate
|
||||
|
||||
# test the environment
|
||||
echo "Environment Info:"
|
||||
which git
|
||||
git --version
|
||||
|
||||
which conda
|
||||
conda --version
|
||||
|
||||
echo ""
|
||||
|
||||
# activate the environment
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script)
|
||||
|
||||
conda activate ./stable-diffusion/env
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
# set the PYTHONPATH
|
||||
cd stable-diffusion
|
||||
SD_PATH=`pwd`
|
||||
export PYTHONPATH="$SD_PATH:$SD_PATH/env/lib/python3.8/site-packages"
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
cd ..
|
||||
|
||||
# done
|
||||
|
||||
echo ""
|
||||
else
|
||||
file_name=$(basename "${BASH_SOURCE[0]}")
|
||||
bash --init-file "$file_name"
|
||||
fi
|
32
scripts/functions.sh
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# utility functions for all scripts
|
||||
#
|
||||
|
||||
fail() {
|
||||
echo
|
||||
echo "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
|
||||
echo
|
||||
if [ "$1" != "" ]; then
|
||||
echo ERROR: $1
|
||||
else
|
||||
echo An error occurred.
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
Error downloading Stable Diffusion UI. Sorry about that, please try to:
|
||||
1. Run this installer again.
|
||||
2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
||||
3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB
|
||||
4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
EOF
|
||||
read -p "Press any key to continue"
|
||||
exit 1
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
61
scripts/on_env_start.bat
Normal file
@ -0,0 +1,61 @@
|
||||
@echo off
|
||||
|
||||
@echo. & echo "Stable Diffusion UI - v2" & echo.
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
if exist "scripts\config.bat" (
|
||||
@call scripts\config.bat
|
||||
)
|
||||
|
||||
if "%update_branch%"=="" (
|
||||
set update_branch=main
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
for /f "tokens=*" %%a in ('python -c "import os; parts = os.getcwd().split(os.path.sep); print(len(parts))"') do if "%%a" NEQ "2" (
|
||||
echo. & echo "!!!! WARNING !!!!" & echo.
|
||||
echo "Your 'stable-diffusion-ui' folder is at %cd%" & echo.
|
||||
echo "The 'stable-diffusion-ui' folder needs to be at the top of your drive, for e.g. 'C:\stable-diffusion-ui' or 'D:\stable-diffusion-ui' etc."
|
||||
echo "Not placing this folder at the top of a drive can cause errors on some computers."
|
||||
echo. & echo "Recommended: Please close this window and move the 'stable-diffusion-ui' folder to the top of a drive. For e.g. 'C:\stable-diffusion-ui'. Then run the installer again." & echo.
|
||||
echo "Not Recommended: If you're sure that you want to install at the current location, please press any key to continue." & echo.
|
||||
|
||||
pause
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_ui_git_cloned" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Stable Diffusion UI's git repository was already installed. Updating from %update_branch%.."
|
||||
|
||||
@cd sd-ui-files
|
||||
|
||||
@call git reset --hard
|
||||
@call git -c advice.detachedHead=false checkout "%update_branch%"
|
||||
@call git pull
|
||||
|
||||
@cd ..
|
||||
) else (
|
||||
@echo. & echo "Downloading Stable Diffusion UI.." & echo.
|
||||
@echo "Using the %update_branch% channel" & echo.
|
||||
|
||||
@call git clone -b "%update_branch%" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files && (
|
||||
@echo sd_ui_git_cloned >> scripts\install_status.txt
|
||||
) || (
|
||||
@echo "Error downloading Stable Diffusion UI. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
@exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@xcopy sd-ui-files\ui ui /s /i /Y /q
|
||||
@copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y
|
||||
@copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
|
||||
@copy "sd-ui-files\scripts\Developer Console.cmd" . /Y
|
||||
|
||||
@call scripts\on_sd_start.bat
|
||||
|
||||
@pause
|
45
scripts/on_env_start.sh
Executable file
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
printf "\n\nStable Diffusion UI\n\n"
|
||||
|
||||
if [ -f "scripts/config.sh" ]; then
|
||||
source scripts/config.sh
|
||||
fi
|
||||
|
||||
if [ "$update_branch" == "" ]; then
|
||||
export update_branch="main"
|
||||
fi
|
||||
|
||||
if [ -f "scripts/install_status.txt" ] && [ `grep -c sd_ui_git_cloned scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Stable Diffusion UI's git repository was already installed. Updating from $update_branch.."
|
||||
|
||||
cd sd-ui-files
|
||||
|
||||
git reset --hard
|
||||
git -c advice.detachedHead=false checkout "$update_branch"
|
||||
git pull
|
||||
|
||||
cd ..
|
||||
else
|
||||
printf "\n\nDownloading Stable Diffusion UI..\n\n"
|
||||
printf "Using the $update_branch channel\n\n"
|
||||
|
||||
if git clone -b "$update_branch" https://github.com/cmdr2/stable-diffusion-ui.git sd-ui-files ; then
|
||||
echo sd_ui_git_cloned >> scripts/install_status.txt
|
||||
else
|
||||
fail "git clone failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf ui
|
||||
cp -Rf sd-ui-files/ui .
|
||||
cp sd-ui-files/scripts/on_sd_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
cp sd-ui-files/scripts/start.sh .
|
||||
cp sd-ui-files/scripts/developer_console.sh .
|
||||
|
||||
./scripts/on_sd_start.sh
|
||||
|
||||
read -p "Press any key to continue"
|
392
scripts/on_sd_start.bat
Normal file
@ -0,0 +1,392 @@
|
||||
@echo off
|
||||
|
||||
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y
|
||||
|
||||
if exist "%cd%\profile" (
|
||||
set USERPROFILE=%cd%\profile
|
||||
)
|
||||
|
||||
@mkdir tmp
|
||||
@set TMP=%cd%\tmp
|
||||
@set TEMP=%cd%\tmp
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "Error activating conda for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@REM remove the old version of the dev console script, if it's still present
|
||||
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
|
||||
@call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
|
||||
|
||||
if NOT DEFINED test_sd2 set test_sd2=N
|
||||
|
||||
@>nul findstr /m "sd_git_cloned" scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Stable Diffusion's git repository was already installed. Updating.."
|
||||
|
||||
@cd stable-diffusion
|
||||
|
||||
@call git remote set-url origin https://github.com/easydiffusion/diffusion-kit.git
|
||||
|
||||
@call git reset --hard
|
||||
@call git pull
|
||||
|
||||
if "%test_sd2%" == "N" (
|
||||
@call git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
|
||||
)
|
||||
if "%test_sd2%" == "Y" (
|
||||
@call git -c advice.detachedHead=false checkout 5d647c5459f4cd790672512222bc41903c01bb71
|
||||
)
|
||||
|
||||
@cd ..
|
||||
) else (
|
||||
@echo. & echo "Downloading Stable Diffusion.." & echo.
|
||||
|
||||
@call git clone https://github.com/easydiffusion/diffusion-kit.git stable-diffusion && (
|
||||
@echo sd_git_cloned >> scripts\install_status.txt
|
||||
) || (
|
||||
@echo "Error downloading Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
@exit /b
|
||||
)
|
||||
|
||||
@cd stable-diffusion
|
||||
@call git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
|
||||
|
||||
@cd ..
|
||||
)
|
||||
|
||||
@cd stable-diffusion
|
||||
|
||||
@>nul findstr /m "conda_sd_env_created" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Packages necessary for Stable Diffusion were already installed"
|
||||
|
||||
@call conda activate .\env
|
||||
) else (
|
||||
@echo. & echo "Downloading packages necessary for Stable Diffusion.." & echo. & echo "***** This will take some time (depending on the speed of the Internet connection) and may appear to be stuck, but please be patient ***** .." & echo.
|
||||
|
||||
@rmdir /s /q .\env
|
||||
|
||||
@REM prevent conda from using packages from the user's home directory, to avoid conflicts
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@call conda env create --prefix env -f environment.yaml || (
|
||||
@echo. & echo "Error installing the packages necessary for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@call conda activate .\env
|
||||
|
||||
for /f "tokens=*" %%a in ('python -c "import torch; import ldm; import transformers; import numpy; import antlr4; print(42)"') do if "%%a" NEQ "42" (
|
||||
@echo. & echo "Dependency test failed! Error installing the packages necessary for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@echo conda_sd_env_created >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
set PATH=C:\Windows\System32;%PATH%
|
||||
|
||||
@>nul findstr /m "conda_sd_gfpgan_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Packages necessary for GFPGAN (Face Correction) were already installed"
|
||||
) else (
|
||||
@echo. & echo "Downloading packages necessary for GFPGAN (Face Correction).." & echo.
|
||||
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
for /f "tokens=*" %%a in ('python -c "from gfpgan import GFPGANer; print(42)"') do if "%%a" NEQ "42" (
|
||||
@echo. & echo "Dependency test failed! Error installing the packages necessary for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@echo conda_sd_gfpgan_deps_installed >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_esrgan_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
@echo "Packages necessary for ESRGAN (Resolution Upscaling) were already installed"
|
||||
) else (
|
||||
@echo. & echo "Downloading packages necessary for ESRGAN (Resolution Upscaling).." & echo.
|
||||
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
for /f "tokens=*" %%a in ('python -c "from basicsr.archs.rrdbnet_arch import RRDBNet; from realesrgan import RealESRGANer; print(42)"') do if "%%a" NEQ "42" (
|
||||
@echo. & echo "Dependency test failed! Error installing the packages necessary for ESRGAN (Resolution Upscaling). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@echo conda_sd_esrgan_deps_installed >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" EQU "0" (
|
||||
echo "Packages necessary for Stable Diffusion UI were already installed"
|
||||
) else (
|
||||
@echo. & echo "Downloading packages necessary for Stable Diffusion UI.." & echo.
|
||||
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@call conda install -c conda-forge -y --prefix env uvicorn fastapi || (
|
||||
echo "Error installing the packages necessary for Stable Diffusion UI. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
call WHERE uvicorn > .tmp
|
||||
@>nul findstr /m "uvicorn" .tmp
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
@>nul 2>nul call python -m picklescan --help
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo Picklescan not found. Installing
|
||||
@call pip install picklescan || (
|
||||
echo "Error installing the picklescan package necessary for Stable Diffusion UI. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
|
||||
|
||||
if not exist "..\models\stable-diffusion" mkdir "..\models\stable-diffusion"
|
||||
if not exist "..\models\vae" mkdir "..\models\vae"
|
||||
echo. > "..\models\stable-diffusion\Put your custom ckpt files here.txt"
|
||||
echo. > "..\models\vae\Put your VAE files here.txt"
|
||||
|
||||
@if exist "sd-v1-4.ckpt" (
|
||||
for %%I in ("sd-v1-4.ckpt") do if "%%~zI" EQU "4265380512" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 4 GB Model."
|
||||
) else (
|
||||
for %%J in ("sd-v1-4.ckpt") do if "%%~zJ" EQU "7703807346" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 7 GB Model."
|
||||
) else (
|
||||
for %%K in ("sd-v1-4.ckpt") do if "%%~zK" EQU "7703810927" (
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the Waifu Model."
|
||||
) else (
|
||||
echo. & echo "The model file present at %cd%\sd-v1-4.ckpt is invalid. It is only %%~zK bytes in size. Re-downloading.." & echo.
|
||||
del "sd-v1-4.ckpt"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "sd-v1-4.ckpt" (
|
||||
@echo. & echo "Downloading data files (weights) for Stable Diffusion.." & echo.
|
||||
|
||||
@call curl -L -k https://me.cmdr2.org/stable-diffusion-ui/sd-v1-4.ckpt > sd-v1-4.ckpt
|
||||
|
||||
@if exist "sd-v1-4.ckpt" (
|
||||
for %%I in ("sd-v1-4.ckpt") do if "%%~zI" NEQ "4265380512" (
|
||||
echo. & echo "Error: The downloaded model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "GFPGANv1.3.pth" (
|
||||
for %%I in ("GFPGANv1.3.pth") do if "%%~zI" EQU "348632874" (
|
||||
echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The GFPGAN model file present at %cd%\GFPGANv1.3.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "GFPGANv1.3.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "GFPGANv1.3.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for GFPGAN (Face Correction).." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > GFPGANv1.3.pth
|
||||
|
||||
@if exist "GFPGANv1.3.pth" (
|
||||
for %%I in ("GFPGANv1.3.pth") do if "%%~zI" NEQ "348632874" (
|
||||
echo. & echo "Error: The downloaded GFPGAN model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "RealESRGAN_x4plus.pth" (
|
||||
for %%I in ("RealESRGAN_x4plus.pth") do if "%%~zI" EQU "67040989" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The RealESRGAN model file present at %cd%\RealESRGAN_x4plus.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "RealESRGAN_x4plus.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "RealESRGAN_x4plus.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > RealESRGAN_x4plus.pth
|
||||
|
||||
@if exist "RealESRGAN_x4plus.pth" (
|
||||
for %%I in ("RealESRGAN_x4plus.pth") do if "%%~zI" NEQ "67040989" (
|
||||
echo. & echo "Error: The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "RealESRGAN_x4plus_anime_6B.pth" (
|
||||
for %%I in ("RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" EQU "17938799" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The RealESRGAN model file present at %cd%\RealESRGAN_x4plus_anime_6B.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "RealESRGAN_x4plus_anime_6B.pth"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "RealESRGAN_x4plus_anime_6B.pth" (
|
||||
@echo. & echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.." & echo.
|
||||
|
||||
@call curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > RealESRGAN_x4plus_anime_6B.pth
|
||||
|
||||
@if exist "RealESRGAN_x4plus_anime_6B.pth" (
|
||||
for %%I in ("RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" NEQ "17938799" (
|
||||
echo. & echo "Error: The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" EQU "334695179" (
|
||||
echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The default VAE (sd-vae-ft-mse-original) file present at models\vae\vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt"
|
||||
)
|
||||
)
|
||||
|
||||
@if not exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
@echo. & echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).." & echo.
|
||||
|
||||
@call curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt
|
||||
|
||||
@if exist "..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt" (
|
||||
for %%I in ("..\models\vae\vae-ft-mse-840000-ema-pruned.ckpt") do if "%%~zI" NEQ "334695179" (
|
||||
echo. & echo "Error: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: %%~zI" & echo.
|
||||
echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
) else (
|
||||
@echo. & echo "Error downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
if "%test_sd2%" == "Y" (
|
||||
@call pip install open_clip_torch==2.0.2
|
||||
)
|
||||
|
||||
@>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo sd_weights_downloaded >> ..\scripts\install_status.txt
|
||||
@echo sd_install_complete >> ..\scripts\install_status.txt
|
||||
)
|
||||
|
||||
@echo. & echo "Stable Diffusion is ready!" & echo.
|
||||
|
||||
@set SD_DIR=%cd%
|
||||
|
||||
@cd env\lib\site-packages
|
||||
@set PYTHONPATH=%SD_DIR%;%cd%
|
||||
@cd ..\..\..
|
||||
@echo PYTHONPATH=%PYTHONPATH%
|
||||
|
||||
call where python
|
||||
call python --version
|
||||
|
||||
@cd ..
|
||||
@set SD_UI_PATH=%cd%\ui
|
||||
@cd stable-diffusion
|
||||
|
||||
@rem
|
||||
@rem Rewrite easy-install.pth. This fixes the installation if the user has relocated the SDUI installation
|
||||
@rem
|
||||
>env\Lib\site-packages\easy-install.pth echo %cd%\src\taming-transformers
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\clip
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\gfpgan
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\realesrgan
|
||||
|
||||
@if NOT DEFINED SD_UI_BIND_PORT set SD_UI_BIND_PORT=9000
|
||||
@if NOT DEFINED SD_UI_BIND_IP set SD_UI_BIND_IP=0.0.0.0
|
||||
@uvicorn server:app --app-dir "%SD_UI_PATH%" --port %SD_UI_BIND_PORT% --host %SD_UI_BIND_IP%
|
||||
|
||||
|
||||
@pause
|
323
scripts/on_sd_start.sh
Executable file
@ -0,0 +1,323 @@
|
||||
#!/bin/bash
|
||||
|
||||
source ./scripts/functions.sh
|
||||
|
||||
cp sd-ui-files/scripts/on_env_start.sh scripts/
|
||||
cp sd-ui-files/scripts/bootstrap.sh scripts/
|
||||
|
||||
# activate the installer env
|
||||
CONDA_BASEPATH=$(conda info --base)
|
||||
source "$CONDA_BASEPATH/etc/profile.d/conda.sh" # avoids the 'shell not initialized' error
|
||||
|
||||
conda activate || fail "Failed to activate conda"
|
||||
|
||||
# remove the old version of the dev console script, if it's still present
|
||||
if [ -e "open_dev_console.sh" ]; then
|
||||
rm "open_dev_console.sh"
|
||||
fi
|
||||
|
||||
python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
|
||||
|
||||
# Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
# Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
if [ "$test_sd2" == "" ]; then
|
||||
export test_sd2="N"
|
||||
fi
|
||||
|
||||
if [ -e "scripts/install_status.txt" ] && [ `grep -c sd_git_cloned scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Stable Diffusion's git repository was already installed. Updating.."
|
||||
|
||||
cd stable-diffusion
|
||||
|
||||
git remote set-url origin https://github.com/easydiffusion/diffusion-kit.git
|
||||
|
||||
git reset --hard
|
||||
git pull
|
||||
|
||||
if [ "$test_sd2" == "N" ]; then
|
||||
git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
|
||||
elif [ "$test_sd2" == "Y" ]; then
|
||||
git -c advice.detachedHead=false checkout 5d647c5459f4cd790672512222bc41903c01bb71
|
||||
fi
|
||||
|
||||
cd ..
|
||||
else
|
||||
printf "\n\nDownloading Stable Diffusion..\n\n"
|
||||
|
||||
if git clone https://github.com/easydiffusion/diffusion-kit.git stable-diffusion ; then
|
||||
echo sd_git_cloned >> scripts/install_status.txt
|
||||
else
|
||||
fail "git clone of basujindal/stable-diffusion.git failed"
|
||||
fi
|
||||
|
||||
cd stable-diffusion
|
||||
git -c advice.detachedHead=false checkout 7f32368ed1030a6e710537047bacd908adea183a
|
||||
|
||||
cd ..
|
||||
fi
|
||||
|
||||
cd stable-diffusion
|
||||
|
||||
if [ `grep -c conda_sd_env_created ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Packages necessary for Stable Diffusion were already installed"
|
||||
|
||||
conda activate ./env || fail "conda activate failed"
|
||||
else
|
||||
printf "\n\nDownloading packages necessary for Stable Diffusion..\n"
|
||||
printf "\n\n***** This will take some time (depending on the speed of the Internet connection) and may appear to be stuck, but please be patient ***** ..\n\n"
|
||||
|
||||
# prevent conda from using packages from the user's home directory, to avoid conflicts
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$(pwd):$(pwd)/env/lib/site-packages"
|
||||
|
||||
if conda env create --prefix env --force -f environment.yaml ; then
|
||||
echo "Installed. Testing.."
|
||||
else
|
||||
fail "'conda env create' failed"
|
||||
fi
|
||||
|
||||
conda activate ./env || fail "conda activate failed"
|
||||
|
||||
out_test=`python -c "import torch; import ldm; import transformers; import numpy; import antlr4; print(42)"`
|
||||
if [ "$out_test" != "42" ]; then
|
||||
fail "Dependency test failed"
|
||||
fi
|
||||
|
||||
echo conda_sd_env_created >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
if [ `grep -c conda_sd_gfpgan_deps_installed ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Packages necessary for GFPGAN (Face Correction) were already installed"
|
||||
else
|
||||
printf "\n\nDownloading packages necessary for GFPGAN (Face Correction)..\n"
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$(pwd):$(pwd)/env/lib/site-packages"
|
||||
|
||||
out_test=`python -c "from gfpgan import GFPGANer; print(42)"`
|
||||
if [ "$out_test" != "42" ]; then
|
||||
echo "EE The dependency check has failed. This usually means that some system libraries are missing."
|
||||
echo "EE On Debian/Ubuntu systems, this are often these packages: libsm6 libxext6 libxrender-dev"
|
||||
echo "EE Other Linux distributions might have different package names for these libraries."
|
||||
fail "GFPGAN dependency test failed"
|
||||
fi
|
||||
|
||||
echo conda_sd_gfpgan_deps_installed >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
if [ `grep -c conda_sd_esrgan_deps_installed ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Packages necessary for ESRGAN (Resolution Upscaling) were already installed"
|
||||
else
|
||||
printf "\n\nDownloading packages necessary for ESRGAN (Resolution Upscaling)..\n"
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$(pwd):$(pwd)/env/lib/site-packages"
|
||||
|
||||
out_test=`python -c "from basicsr.archs.rrdbnet_arch import RRDBNet; from realesrgan import RealESRGANer; print(42)"`
|
||||
if [ "$out_test" != "42" ]; then
|
||||
fail "ESRGAN dependency test failed"
|
||||
fi
|
||||
|
||||
echo conda_sd_esrgan_deps_installed >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
if [ `grep -c conda_sd_ui_deps_installed ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo "Packages necessary for Stable Diffusion UI were already installed"
|
||||
else
|
||||
printf "\n\nDownloading packages necessary for Stable Diffusion UI..\n\n"
|
||||
|
||||
export PYTHONNOUSERSITE=1
|
||||
export PYTHONPATH="$(pwd):$(pwd)/env/lib/site-packages"
|
||||
|
||||
if conda install -c conda-forge --prefix ./env -y uvicorn fastapi ; then
|
||||
echo "Installed. Testing.."
|
||||
else
|
||||
fail "'conda install uvicorn' failed"
|
||||
fi
|
||||
|
||||
if ! command -v uvicorn &> /dev/null; then
|
||||
fail "UI packages not found!"
|
||||
fi
|
||||
|
||||
echo conda_sd_ui_deps_installed >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
if python -m picklescan --help >/dev/null 2>&1; then
|
||||
echo "Picklescan is already installed."
|
||||
else
|
||||
echo "Picklescan not found, installing."
|
||||
pip install picklescan || fail "Picklescan installation failed."
|
||||
fi
|
||||
|
||||
|
||||
|
||||
mkdir -p "../models/stable-diffusion"
|
||||
mkdir -p "../models/vae"
|
||||
echo "" > "../models/stable-diffusion/Put your custom ckpt files here.txt"
|
||||
echo "" > "../models/vae/Put your VAE files here.txt"
|
||||
|
||||
if [ -f "sd-v1-4.ckpt" ]; then
|
||||
model_size=`find "sd-v1-4.ckpt" -printf "%s"`
|
||||
|
||||
if [ "$model_size" -eq "4265380512" ] || [ "$model_size" -eq "7703807346" ] || [ "$model_size" -eq "7703810927" ]; then
|
||||
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at $PWD/sd-v1-4.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm sd-v1-4.ckpt
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "sd-v1-4.ckpt" ]; then
|
||||
echo "Downloading data files (weights) for Stable Diffusion.."
|
||||
|
||||
curl -L -k https://me.cmdr2.org/stable-diffusion-ui/sd-v1-4.ckpt > sd-v1-4.ckpt
|
||||
|
||||
if [ -f "sd-v1-4.ckpt" ]; then
|
||||
model_size=`find "sd-v1-4.ckpt" -printf "%s"`
|
||||
if [ ! "$model_size" == "4265380512" ]; then
|
||||
fail "The downloaded model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for Stable Diffusion"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "GFPGANv1.3.pth" ]; then
|
||||
model_size=`find "GFPGANv1.3.pth" -printf "%s"`
|
||||
|
||||
if [ "$model_size" -eq "348632874" ]; then
|
||||
echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at $PWD/GFPGANv1.3.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm GFPGANv1.3.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "GFPGANv1.3.pth" ]; then
|
||||
echo "Downloading data files (weights) for GFPGAN (Face Correction).."
|
||||
|
||||
curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > GFPGANv1.3.pth
|
||||
|
||||
if [ -f "GFPGANv1.3.pth" ]; then
|
||||
model_size=`find "GFPGANv1.3.pth" -printf "%s"`
|
||||
if [ ! "$model_size" -eq "348632874" ]; then
|
||||
fail "The downloaded GFPGAN model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for GFPGAN (Face Correction)."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "RealESRGAN_x4plus.pth" ]; then
|
||||
model_size=`find "RealESRGAN_x4plus.pth" -printf "%s"`
|
||||
|
||||
if [ "$model_size" -eq "67040989" ]; then
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at $PWD/RealESRGAN_x4plus.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm RealESRGAN_x4plus.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "RealESRGAN_x4plus.pth" ]; then
|
||||
echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus.."
|
||||
|
||||
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > RealESRGAN_x4plus.pth
|
||||
|
||||
if [ -f "RealESRGAN_x4plus.pth" ]; then
|
||||
model_size=`find "RealESRGAN_x4plus.pth" -printf "%s"`
|
||||
if [ ! "$model_size" -eq "67040989" ]; then
|
||||
fail "The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
model_size=`find "RealESRGAN_x4plus_anime_6B.pth" -printf "%s"`
|
||||
|
||||
if [ "$model_size" -eq "17938799" ]; then
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at $PWD/RealESRGAN_x4plus_anime_6B.pth is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm RealESRGAN_x4plus_anime_6B.pth
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
echo "Downloading data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime.."
|
||||
|
||||
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > RealESRGAN_x4plus_anime_6B.pth
|
||||
|
||||
if [ -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
|
||||
model_size=`find "RealESRGAN_x4plus_anime_6B.pth" -printf "%s"`
|
||||
if [ ! "$model_size" -eq "17938799" ]; then
|
||||
fail "The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: $model_size"
|
||||
fi
|
||||
else
|
||||
fail "Error downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
model_size=`find ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt -printf "%s"`
|
||||
|
||||
if [ "$model_size" -eq "334695179" ]; then
|
||||
echo "Data files (weights) necessary for the default VAE (sd-vae-ft-mse-original) were already downloaded"
|
||||
else
|
||||
printf "\n\nThe model file present at models/vae/vae-ft-mse-840000-ema-pruned.ckpt is invalid. It is only $model_size bytes in size. Re-downloading.."
|
||||
rm ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
echo "Downloading data files (weights) for the default VAE (sd-vae-ft-mse-original).."
|
||||
|
||||
curl -L -k https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.ckpt > ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt
|
||||
|
||||
if [ -f "../models/vae/vae-ft-mse-840000-ema-pruned.ckpt" ]; then
|
||||
model_size=`find ../models/vae/vae-ft-mse-840000-ema-pruned.ckpt -printf "%s"`
|
||||
if [ ! "$model_size" -eq "334695179" ]; then
|
||||
printf "\n\nError: The downloaded default VAE (sd-vae-ft-mse-original) file was invalid! Bytes downloaded: $model_size\n\n"
|
||||
printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
|
||||
read -p "Press any key to continue"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
printf "\n\nError downloading the data files (weights) for the default VAE (sd-vae-ft-mse-original). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
|
||||
read -p "Press any key to continue"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$test_sd2" == "Y" ]; then
|
||||
pip install open_clip_torch==2.0.2
|
||||
fi
|
||||
|
||||
if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then
|
||||
echo sd_weights_downloaded >> ../scripts/install_status.txt
|
||||
echo sd_install_complete >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
printf "\n\nStable Diffusion is ready!\n\n"
|
||||
|
||||
SD_PATH=`pwd`
|
||||
export PYTHONPATH="$SD_PATH:$SD_PATH/env/lib/python3.8/site-packages"
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
|
||||
which python
|
||||
python --version
|
||||
|
||||
cd ..
|
||||
export SD_UI_PATH=`pwd`/ui
|
||||
cd stable-diffusion
|
||||
|
||||
uvicorn server:app --app-dir "$SD_UI_PATH" --port ${SD_UI_BIND_PORT:-9000} --host ${SD_UI_BIND_IP:-0.0.0.0}
|
||||
|
||||
read -p "Press any key to continue"
|
6
scripts/post_activate.bat
Normal file
@ -0,0 +1,6 @@
|
||||
@call conda --version
|
||||
@call git --version
|
||||
|
||||
cd %CONDA_PREFIX%\..\scripts
|
||||
|
||||
on_env_start.bat
|
12
scripts/post_activate.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
conda-unpack
|
||||
|
||||
source $CONDA_PREFIX/etc/profile.d/conda.sh
|
||||
|
||||
conda --version
|
||||
git --version
|
||||
|
||||
cd $CONDA_PREFIX/../scripts
|
||||
|
||||
./on_env_start.sh
|
22
scripts/start.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
# set legacy installer's PATH, if it exists
|
||||
if [ -e "installer" ]; then export PATH="$(pwd)/installer/bin:$PATH"; fi
|
||||
|
||||
# Setup the packages required for the installer
|
||||
scripts/bootstrap.sh || exit 1
|
||||
|
||||
# set new installer's PATH, if it downloaded any packages
|
||||
if [ -e "installer_files/env" ]; then export PATH="$(pwd)/installer_files/env/bin:$PATH"; fi
|
||||
|
||||
# Test the bootstrap
|
||||
which git
|
||||
git --version || exit 1
|
||||
|
||||
which conda
|
||||
conda --version || exit 1
|
||||
|
||||
# Download the rest of the installer and UI
|
||||
scripts/on_env_start.sh
|
2
scripts/win_enable_long_filepaths.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Type DWord -Value 1
|
||||
pause
|
3
server
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Please use 'docker-compose up &' to start the server, and 'docker-compose down' to stop the server"
|
@ -0,0 +1,171 @@
|
||||
{
|
||||
"_name_or_path": "clip-vit-large-patch14/",
|
||||
"architectures": [
|
||||
"CLIPModel"
|
||||
],
|
||||
"initializer_factor": 1.0,
|
||||
"logit_scale_init_value": 2.6592,
|
||||
"model_type": "clip",
|
||||
"projection_dim": 768,
|
||||
"text_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": 0,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": 2,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 768,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 3072,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"max_position_embeddings": 77,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_text_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 12,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 12,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": 1,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false,
|
||||
"vocab_size": 49408
|
||||
},
|
||||
"text_config_dict": {
|
||||
"hidden_size": 768,
|
||||
"intermediate_size": 3072,
|
||||
"num_attention_heads": 12,
|
||||
"num_hidden_layers": 12,
|
||||
"projection_dim": 768
|
||||
},
|
||||
"torch_dtype": "float32",
|
||||
"transformers_version": null,
|
||||
"vision_config": {
|
||||
"_name_or_path": "",
|
||||
"add_cross_attention": false,
|
||||
"architectures": null,
|
||||
"attention_dropout": 0.0,
|
||||
"bad_words_ids": null,
|
||||
"bos_token_id": null,
|
||||
"chunk_size_feed_forward": 0,
|
||||
"cross_attention_hidden_size": null,
|
||||
"decoder_start_token_id": null,
|
||||
"diversity_penalty": 0.0,
|
||||
"do_sample": false,
|
||||
"dropout": 0.0,
|
||||
"early_stopping": false,
|
||||
"encoder_no_repeat_ngram_size": 0,
|
||||
"eos_token_id": null,
|
||||
"finetuning_task": null,
|
||||
"forced_bos_token_id": null,
|
||||
"forced_eos_token_id": null,
|
||||
"hidden_act": "quick_gelu",
|
||||
"hidden_size": 1024,
|
||||
"id2label": {
|
||||
"0": "LABEL_0",
|
||||
"1": "LABEL_1"
|
||||
},
|
||||
"image_size": 224,
|
||||
"initializer_factor": 1.0,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 4096,
|
||||
"is_decoder": false,
|
||||
"is_encoder_decoder": false,
|
||||
"label2id": {
|
||||
"LABEL_0": 0,
|
||||
"LABEL_1": 1
|
||||
},
|
||||
"layer_norm_eps": 1e-05,
|
||||
"length_penalty": 1.0,
|
||||
"max_length": 20,
|
||||
"min_length": 0,
|
||||
"model_type": "clip_vision_model",
|
||||
"no_repeat_ngram_size": 0,
|
||||
"num_attention_heads": 16,
|
||||
"num_beam_groups": 1,
|
||||
"num_beams": 1,
|
||||
"num_hidden_layers": 24,
|
||||
"num_return_sequences": 1,
|
||||
"output_attentions": false,
|
||||
"output_hidden_states": false,
|
||||
"output_scores": false,
|
||||
"pad_token_id": null,
|
||||
"patch_size": 14,
|
||||
"prefix": null,
|
||||
"problem_type": null,
|
||||
"projection_dim" : 768,
|
||||
"pruned_heads": {},
|
||||
"remove_invalid_values": false,
|
||||
"repetition_penalty": 1.0,
|
||||
"return_dict": true,
|
||||
"return_dict_in_generate": false,
|
||||
"sep_token_id": null,
|
||||
"task_specific_params": null,
|
||||
"temperature": 1.0,
|
||||
"tie_encoder_decoder": false,
|
||||
"tie_word_embeddings": true,
|
||||
"tokenizer_class": null,
|
||||
"top_k": 50,
|
||||
"top_p": 1.0,
|
||||
"torch_dtype": null,
|
||||
"torchscript": false,
|
||||
"transformers_version": "4.16.0.dev0",
|
||||
"use_bfloat16": false
|
||||
},
|
||||
"vision_config_dict": {
|
||||
"hidden_size": 1024,
|
||||
"intermediate_size": 4096,
|
||||
"num_attention_heads": 16,
|
||||
"num_hidden_layers": 24,
|
||||
"patch_size": 14,
|
||||
"projection_dim": 768
|
||||
}
|
||||
}
|
374
ui/index.html
Normal file
@ -0,0 +1,374 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stable Diffusion UI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#673AB6">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="stylesheet" href="/media/css/fonts.css">
|
||||
<link rel="stylesheet" href="/media/css/themes.css">
|
||||
<link rel="stylesheet" href="/media/css/main.css">
|
||||
<link rel="stylesheet" href="/media/css/auto-save.css">
|
||||
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css">
|
||||
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css">
|
||||
<link rel="stylesheet" href="/media/css/drawingboard.min.css">
|
||||
<link rel="stylesheet" href="/media/css/jquery-confirm.min.css">
|
||||
<link rel="manifest" href="/media/manifest.webmanifest">
|
||||
<script src="/media/js/jquery-3.6.1.min.js"></script>
|
||||
<script src="/media/js/jquery-confirm.min.js"></script>
|
||||
<script src="/media/js/drawingboard.min.js"></script>
|
||||
<script src="/media/js/marked.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="top-nav">
|
||||
<div id="logo">
|
||||
<h1>
|
||||
Stable Diffusion UI
|
||||
<small>v2.4.17 <span id="updateBranchLabel"></span></small>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="server-status">
|
||||
<div id="server-status-color">●</div>
|
||||
<span id="server-status-msg">Stable Diffusion is starting..</span>
|
||||
</div>
|
||||
<div id="tab-container">
|
||||
<span id="tab-main" class="tab active">
|
||||
<span><i class="fa fa-image icon"></i> Generate</span>
|
||||
</span>
|
||||
<span id="tab-settings" class="tab">
|
||||
<span><i class="fa fa-gear icon"></i> Settings</span>
|
||||
</span>
|
||||
<span id="tab-about" class="tab">
|
||||
<span><i class="fa fa-comments icon"></i> Help & Community</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-content-wrapper">
|
||||
<div id="tab-content-main" class="tab-content active flex-container">
|
||||
<div id="editor">
|
||||
<div id="editor-inputs">
|
||||
<div id="editor-inputs-prompt" class="row">
|
||||
<label for="prompt"><b>Enter Prompt</b></label> <small>or</small> <button id="promptsFromFileBtn">Load from a file</button>
|
||||
<textarea id="prompt" class="col-free">a photograph of an astronaut riding a horse</textarea>
|
||||
<input id="prompt_from_file" name="prompt_from_file" type="file" /> <!-- hidden -->
|
||||
<label for="negative_prompt" class="collapsible" id="negative_prompt_handle">
|
||||
Negative Prompt
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-prompts#negative-prompts" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about Negative Prompts</span></i></a>
|
||||
<small>(optional)</small>
|
||||
</label>
|
||||
<div class="collapsible-content">
|
||||
<textarea id="negative_prompt" name="negative_prompt" placeholder="list the things to remove from the image (e.g. fog, green)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor-inputs-init-image" class="row">
|
||||
<label for="init_image">Initial Image (img2img) <small>(optional)</small> </label> <input id="init_image" name="init_image" type="file" /><br/>
|
||||
|
||||
<div id="init_image_preview_container" class="image_preview_container">
|
||||
<div id="init_image_wrapper">
|
||||
<img id="init_image_preview" src="" />
|
||||
<span id="init_image_size_box"></span>
|
||||
<button class="init_image_clear image_clear_btn"><i class="fa-solid fa-xmark"></i></button>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<input id="enable_mask" name="enable_mask" type="checkbox">
|
||||
<label for="enable_mask">
|
||||
In-Painting (beta)
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Inpainting" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about InPainting</span></i></a>
|
||||
<small>(select the area which the AI will paint into)</small>
|
||||
</label>
|
||||
<div id="inpaintingEditor"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor-inputs-tags-container" class="row">
|
||||
<label>Image Modifiers <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">click an Image Modifier to remove it, use Ctrl+Mouse Wheel to adjust its weight</span></i>:</label>
|
||||
<div id="editor-inputs-tags-list"></div>
|
||||
</div>
|
||||
|
||||
<button id="makeImage" class="primaryButton">Make Image</button>
|
||||
<button id="stopImage" class="secondaryButton">Stop All</button>
|
||||
</div>
|
||||
|
||||
<span class="line-separator"></span>
|
||||
|
||||
<div id="editor-settings" class="settings-box panel-box">
|
||||
<h4 class="collapsible">
|
||||
Image Settings
|
||||
<i id="reset-image-settings" class="fa-solid fa-arrow-rotate-left section-button">
|
||||
<span class="simple-tooltip right">
|
||||
Reset Image Settings
|
||||
</span>
|
||||
</i>
|
||||
</h4>
|
||||
<div id="editor-settings-entries" class="collapsible-content">
|
||||
<div><table>
|
||||
<tr><b class="settings-subheader">Image Settings</b></tr>
|
||||
<tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="30000" onkeypress="preventNonNumericalInput(event)"> <input id="random_seed" name="random_seed" type="checkbox" checked><label for="random_seed">Random</label></td></tr>
|
||||
<tr class="pl-5"><td><label for="num_outputs_total">Number of Images:</label></td><td><input id="num_outputs_total" name="num_outputs_total" value="1" size="1" onkeypress="preventNonNumericalInput(event)"> <label><small>(total)</small></label> <input id="num_outputs_parallel" name="num_outputs_parallel" value="1" size="1" onkeypress="preventNonNumericalInput(event)"> <label for="num_outputs_parallel"><small>(in parallel)</small></label></td></tr>
|
||||
<tr class="pl-5"><td><label for="stable_diffusion_model">Model:</label></td><td>
|
||||
<select id="stable_diffusion_model" name="stable_diffusion_model">
|
||||
<!-- <option value="sd-v1-4" selected>sd-v1-4</option> -->
|
||||
</select>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Models" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about custom models</span></i></a>
|
||||
</td></tr>
|
||||
<tr class="pl-5"><td><label for="vae_model">Custom VAE:</i></label></td><td>
|
||||
<select id="vae_model" name="vae_model">
|
||||
<!-- <option value="" selected>None</option> -->
|
||||
</select>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about VAEs</span></i></a>
|
||||
</td></tr>
|
||||
<tr id="samplerSelection" class="pl-5"><td><label for="sampler">Sampler:</label></td><td>
|
||||
<select id="sampler" name="sampler">
|
||||
<option value="plms">plms</option>
|
||||
<option value="ddim">ddim</option>
|
||||
<option value="heun">heun</option>
|
||||
<option value="euler">euler</option>
|
||||
<option value="euler_a" selected>euler_a</option>
|
||||
<option value="dpm2">dpm2</option>
|
||||
<option value="dpm2_a">dpm2_a</option>
|
||||
<option value="lms">lms</option>
|
||||
</select>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use#samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about samplers</span></i></a>
|
||||
</td></tr>
|
||||
<tr class="pl-5"><td><label>Image Size: </label></td><td>
|
||||
<select id="width" name="width" value="512">
|
||||
<option value="128">128 (*)</option>
|
||||
<option value="192">192</option>
|
||||
<option value="256">256 (*)</option>
|
||||
<option value="320">320</option>
|
||||
<option value="384">384</option>
|
||||
<option value="448">448</option>
|
||||
<option value="512" selected>512 (*)</option>
|
||||
<option value="576">576</option>
|
||||
<option value="640">640</option>
|
||||
<option value="704">704</option>
|
||||
<option value="768">768 (*)</option>
|
||||
<option value="832">832</option>
|
||||
<option value="896">896</option>
|
||||
<option value="960">960</option>
|
||||
<option value="1024">1024 (*)</option>
|
||||
<option value="1280">1280</option>
|
||||
<option value="1536">1536</option>
|
||||
<option value="1792">1792</option>
|
||||
<option value="2048">2048</option>
|
||||
</select>
|
||||
<label for="width"><small>(width)</small></label>
|
||||
<select id="height" name="height" value="512">
|
||||
<option value="128">128 (*)</option>
|
||||
<option value="192">192</option>
|
||||
<option value="256">256 (*)</option>
|
||||
<option value="320">320</option>
|
||||
<option value="384">384</option>
|
||||
<option value="448">448</option>
|
||||
<option value="512" selected>512 (*)</option>
|
||||
<option value="576">576</option>
|
||||
<option value="640">640</option>
|
||||
<option value="704">704</option>
|
||||
<option value="768">768 (*)</option>
|
||||
<option value="832">832</option>
|
||||
<option value="896">896</option>
|
||||
<option value="960">960</option>
|
||||
<option value="1024">1024 (*)</option>
|
||||
<option value="1280">1280</option>
|
||||
<option value="1536">1536</option>
|
||||
<option value="1792">1792</option>
|
||||
<option value="2048">2048</option>
|
||||
</select>
|
||||
<label for="height"><small>(height)</small></label>
|
||||
</td></tr>
|
||||
<tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" size="4" value="25" onkeypress="preventNonNumericalInput(event)"></td></tr>
|
||||
<tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="10" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
|
||||
<tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td></tr></span>
|
||||
<tr class="pl-5"><td><label for="output_format">Output Format:</label></td><td>
|
||||
<select id="output_format" name="output_format">
|
||||
<option value="jpeg" selected>jpeg</option>
|
||||
<option value="png">png</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
</table></div>
|
||||
|
||||
<div><ul>
|
||||
<li><b class="settings-subheader">Render Settings</b></li>
|
||||
<li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview <small>(uses more VRAM, and slower image creation)</small></label></li>
|
||||
<li class="pl-5"><input id="use_face_correction" name="use_face_correction" type="checkbox"> <label for="use_face_correction">Fix incorrect faces and eyes <small>(uses GFPGAN)</small></label></li>
|
||||
<li class="pl-5">
|
||||
<input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Upscale image by 4x with </label>
|
||||
<select id="upscale_model" name="upscale_model">
|
||||
<option value="RealESRGAN_x4plus" selected>RealESRGAN_x4plus</option>
|
||||
<option value="RealESRGAN_x4plus_anime_6B">RealESRGAN_x4plus_anime_6B</option>
|
||||
</select>
|
||||
</li>
|
||||
<li class="pl-5"><input id="show_only_filtered_image" name="show_only_filtered_image" type="checkbox" checked> <label for="show_only_filtered_image">Show only the corrected/upscaled image</label></li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editor-modifiers" class="panel-box">
|
||||
<h4 class="collapsible">
|
||||
Image Modifiers (art styles, tags etc)
|
||||
<i id="modifier-settings-btn" class="fa-solid fa-gear section-button">
|
||||
<span class="simple-tooltip left">
|
||||
Add Custom Modifiers
|
||||
</span>
|
||||
</i>
|
||||
</h4>
|
||||
<div id="editor-modifiers-entries" class="collapsible-content">
|
||||
<div id="editor-modifiers-entries-toolbar">
|
||||
<label for="preview-image">Image Style:</label>
|
||||
<select id="preview-image" name="preview-image" value="portrait">
|
||||
<option value="portrait" selected="">Face</option>
|
||||
<option value="landscape">Landscape</option>
|
||||
</select>
|
||||
|
||||
<label for="modifier-card-size-slider">Thumbnail Size:</label>
|
||||
<input id="modifier-card-size-slider" name="modifier-card-size-slider" value="0" type="range" min="-3" max="5">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="preview" class="col-free">
|
||||
<div id="initial-text">
|
||||
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<br/><br/>
|
||||
You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section
|
||||
and selecting the desired modifiers.<br/><br/>
|
||||
Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
|
||||
</div>
|
||||
<div id="preview-tools">
|
||||
<button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can"></i> Clear All</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-content-settings" class="tab-content">
|
||||
<div id="system-settings" class="tab-content-inner">
|
||||
<h1>System Settings</h1>
|
||||
<div class="parameters-table"></div>
|
||||
<br/>
|
||||
<button id="save-system-settings-btn" class="primaryButton">Save</button>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<h3><i class="fa fa-microchip icon"></i> System Info</h3>
|
||||
<div id="system-info">
|
||||
<table>
|
||||
<tr><td><label>Processor:</label></td><td id="system-info-cpu" class="value"></td></tr>
|
||||
<tr><td><label>Compatible Graphics Cards (all):</label></td><td id="system-info-gpus-all" class="value"></td></tr>
|
||||
<tr><td></td><td> </td></tr>
|
||||
<tr><td><label>Used for rendering 🔥:</label></td><td id="system-info-rendering-devices" class="value"></td></tr>
|
||||
<tr><td><label>Server Addresses <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">You can access Stable Diffusion UI from other devices using these addresses</span></i> :</label></td><td id="system-info-server-hosts" class="value"></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-content-about" class="tab-content">
|
||||
<div class="tab-content-inner">
|
||||
<div class="float-container">
|
||||
<div class="float-child">
|
||||
<h1>Help</h1>
|
||||
<ul id="help-links">
|
||||
<li><span class="help-section">Using the software</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/How-To-Use" target="_blank"><i class="fa-solid fa-book fa-fw"></i> How to use</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Overview" target="_blank"><i class="fa-solid fa-list fa-fw"></i> UI Overview</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-Prompts" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> Writing prompts</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Inpainting" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Inpainting</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Run on Multiple GPUs</a>
|
||||
</ul>
|
||||
|
||||
<li><span class="help-section">Installation</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" target="_blank"><i class="fa-solid fa-circle-question fa-fw"></i> Troubleshooting</a>
|
||||
</ul>
|
||||
|
||||
<li><span class="help-section">Downloadable Content</span>
|
||||
<ul>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Custom-Models" target="_blank"><i class="fa-solid fa-images fa-fw"></i> Custom Models</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Plugins" target="_blank"><i class="fa-solid fa-puzzle-piece fa-fw"></i> UI Plugins</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder" target="_blank"><i class="fa-solid fa-hand-sparkles fa-fw"></i> VAE Variational Auto Encoder</a>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="float-child">
|
||||
<h1>Community</h1>
|
||||
<ul id="community-links">
|
||||
<li><a href="https://discord.com/invite/u9yhsFmEkB" target="_blank"><i class="fa-brands fa-discord fa-fw"></i> Discord user community</a></li>
|
||||
<li><a href="https://www.reddit.com/r/StableDiffusionUI/" target="_blank"><i class="fa-brands fa-reddit fa-fw"></i> Reddit community</a></li>
|
||||
<li><a href="https://github.com/cmdr2/stable-diffusion-ui" target="_blank"><i class="fa-brands fa-github fa-fw"></i> Source code on GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="save-settings-config" class="popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Save Settings Configuration</h1>
|
||||
<p>Select which settings should be remembered when restarting the browser</p>
|
||||
<table id="save-settings-config-table" class="form-table">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modifier-settings-config" class="popup">
|
||||
<div>
|
||||
<i class="close-button fa-solid fa-xmark"></i>
|
||||
<h1>Modifier Settings</h1>
|
||||
<p>Set your custom modifiers (one per line)</p>
|
||||
<textarea id="custom-modifiers-input" placeholder="Enter your custom modifiers, one-per-line"></textarea>
|
||||
<p><small><b>Tip:</b> You can include special characters like {} () [] and |. You can also put multiple comma-separated phrases in a single line, to make a single modifier that combines all of those.</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer-spacer"></div>
|
||||
<div id="footer">
|
||||
<div class="line-separator"> </div>
|
||||
<p>If you found this project useful and want to help keep it alive, please <a href="https://ko-fi.com/cmdr2_stablediffusion_ui" target="_blank"><img src="/media/images/kofi.png" id="coffeeButton"></a> to help cover the cost of development and maintenance! Thank you for your support!</p>
|
||||
<p>Please feel free to join the <a href="https://discord.com/invite/u9yhsFmEkB" target="_blank">discord community</a> or <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<div id="footer-legal">
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="media/js/utils.js"></script>
|
||||
<script src="media/js/parameters.js"></script>
|
||||
<script src="media/js/plugins.js"></script>
|
||||
<script src="media/js/inpainting-editor.js"></script>
|
||||
<script src="media/js/image-modifiers.js"></script>
|
||||
<script src="media/js/auto-save.js"></script>
|
||||
<script src="media/js/main.js"></script>
|
||||
<script src="media/js/themes.js"></script>
|
||||
<script src="media/js/dnd.js"></script>
|
||||
<script>
|
||||
async function init() {
|
||||
await initSettings()
|
||||
await getModels()
|
||||
await getDiskPath()
|
||||
await getAppConfig()
|
||||
await loadModifiers()
|
||||
await loadUIPlugins()
|
||||
await getSystemInfo()
|
||||
|
||||
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||
healthCheck()
|
||||
|
||||
playSound()
|
||||
}
|
||||
|
||||
init()
|
||||
</script>
|
||||
</html>
|
81
ui/media/css/auto-save.css
Normal file
@ -0,0 +1,81 @@
|
||||
/* Auto-Settings Styling */
|
||||
#auto_save_settings ~ button {
|
||||
margin: 5px;
|
||||
}
|
||||
#auto_save_settings:not(:checked) ~ button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.form-table th {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.form-table td:first-child > *,
|
||||
.form-table th:first-child > * {
|
||||
float: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.form-table td:last-child > *,
|
||||
.form-table th:last-child > * {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
.parameters-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.parameters-table > div {
|
||||
background: var(--background-color2);
|
||||
display: flex;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.parameters-table small {
|
||||
color: rgb(153, 153, 153);
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(1) {
|
||||
font-size: 20px;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(2) {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.parameters-table > div:first-child {
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
}
|
||||
|
||||
.parameters-table > div:last-child {
|
||||
border-radius: 0px 0px 12px 12px;
|
||||
}
|
||||
|
||||
.parameters-table .fa-fire {
|
||||
color: #F7630C;
|
||||
}
|
5
ui/media/css/drawingboard.min.css
vendored
Normal file
6
ui/media/css/fontawesome-all.min.css
vendored
Normal file
40
ui/media/css/fonts.css
Normal file
@ -0,0 +1,40 @@
|
||||
/* work-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-600.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-600.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-700 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* work-sans-800 - latin */
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: local(''),
|
||||
url('/media/fonts/work-sans-v18-latin-800.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/media/fonts/work-sans-v18-latin-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
9
ui/media/css/jquery-confirm.min.css
vendored
Normal file
1031
ui/media/css/main.css
Normal file
223
ui/media/css/modifier-thumbnails.css
Normal file
@ -0,0 +1,223 @@
|
||||
.modifier-card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
transition: 0.1s;
|
||||
border-radius: 7px;
|
||||
margin: 3pt 3pt;
|
||||
float: left;
|
||||
width: 8em;
|
||||
height: 11.5em;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 8em 3.5em;
|
||||
gap: 0px 0px;
|
||||
grid-auto-flow: row;
|
||||
grid-template-areas:
|
||||
"modifier-card-image-container"
|
||||
"modifier-card-container";
|
||||
border: 2px solid rgba(255, 255, 255, .05);
|
||||
cursor: pointer;
|
||||
}
|
||||
.modifier-card-size_5 {
|
||||
width: 18em;
|
||||
grid-template-rows: 18em 3.5em;
|
||||
height: 21.5em;
|
||||
}
|
||||
.modifier-card-size_5 .modifier-card-image-overlay {
|
||||
font-size: 8em;
|
||||
}
|
||||
.modifier-card-size_4 {
|
||||
width: 14em;
|
||||
grid-template-rows: 14em 3.5em;
|
||||
height: 17.5em;
|
||||
}
|
||||
.modifier-card-size_4 .modifier-card-image-overlay {
|
||||
font-size: 7em;
|
||||
}
|
||||
.modifier-card-size_3 {
|
||||
width: 11em;
|
||||
grid-template-rows: 11em 3.5em;
|
||||
height: 14.5em;
|
||||
}
|
||||
.modifier-card-size_3 .modifier-card-image-overlay {
|
||||
font-size: 6em;
|
||||
}
|
||||
.modifier-card-size_2 {
|
||||
width: 10em;
|
||||
grid-template-rows: 10em 3.5em;
|
||||
height: 13.5em;
|
||||
}
|
||||
.modifier-card-size_2 .modifier-card-image-overlay {
|
||||
font-size: 6em;
|
||||
}
|
||||
.modifier-card-size_1 {
|
||||
width: 9em;
|
||||
grid-template-rows: 9em 3.5em;
|
||||
height: 12.5em;
|
||||
}
|
||||
.modifier-card-size_1 .modifier-card-image-overlay {
|
||||
font-size: 5em;
|
||||
}
|
||||
.modifier-card-size_-1 {
|
||||
width: 7em;
|
||||
grid-template-rows: 7em 3.5em;
|
||||
height: 10.5em;
|
||||
}
|
||||
.modifier-card-size_-1 .modifier-card-image-overlay {
|
||||
font-size: 4em;
|
||||
}
|
||||
.modifier-card-size_-2 {
|
||||
width: 6em;
|
||||
grid-template-rows: 6em 3.5em;
|
||||
height: 9.5em;
|
||||
}
|
||||
.modifier-card-size_-2 .modifier-card-image-overlay {
|
||||
font-size: 3em;
|
||||
}
|
||||
.modifier-card-size_-3 {
|
||||
width: 5em;
|
||||
grid-template-rows: 5em 3.5em;
|
||||
height: 8.5em;
|
||||
}
|
||||
.modifier-card-size_-3 .modifier-card-image-overlay {
|
||||
font-size: 3em;
|
||||
}
|
||||
.modifier-card-size_-3 .modifier-card-label {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.modifier-card-tiny {
|
||||
width: 6em;
|
||||
height: 9.5em;
|
||||
grid-template-rows: 6em 3.5em;
|
||||
}
|
||||
.modifier-card-tiny .modifier-card-image-overlay {
|
||||
font-size: 4em;
|
||||
}
|
||||
.modifier-card:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 5px 16px 5px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.modifier-card-image-container {
|
||||
border-radius: 5px 5px 0 0;
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
grid-area: modifier-card-image-container;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(255 255 255 / 8%);
|
||||
}
|
||||
.modifier-card-image-container img {
|
||||
width: inherit;
|
||||
height: 100%;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.modifier-card-image-container * {
|
||||
position: absolute;
|
||||
}
|
||||
.modifier-card-container {
|
||||
text-align: center;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
border-radius: 0 0 5px 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: modifier-card-container;
|
||||
font-weight: 100;
|
||||
font-size: .9em;
|
||||
width: inherit;
|
||||
}
|
||||
.modifier-card-label {
|
||||
padding: 4px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.modifier-card-image-overlay {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
border-radius: 5px 5px 0 0;
|
||||
opacity: 0;
|
||||
font-size: 5em;
|
||||
font-weight: 900;
|
||||
color: rgb(255 255 255 / 50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modifier-card-overlay {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
.modifier-card:hover > .modifier-card-image-container .modifier-card-image-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
.modifier-card:hover > .modifier-card-image-container img {
|
||||
filter: blur(.1em);
|
||||
}
|
||||
.modifier-card:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 5px 16px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#preview-image {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.modifier-card-active {
|
||||
border: 2px solid rgb(179 82 255 / 94%);
|
||||
box-shadow: 0 0px 10px 0 rgb(170 0 229 / 58%);
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.tooltip .tooltip-text {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background: rgb(101,97,181);
|
||||
background: linear-gradient(180deg, rgba(101,97,181,1) 0%, rgba(47,45,85,1) 100%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 105%;
|
||||
left: 39%;
|
||||
margin-left: -60px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
border: 2px solid rgb(90 100 177 / 94%);
|
||||
box-shadow: 0px 10px 20px 5px rgb(11 0 58 / 55%);
|
||||
width: 10em;
|
||||
}
|
||||
.tooltip .tooltip-text::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -0.9em;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgb(90 100 177 / 94%) transparent;
|
||||
}
|
||||
.tooltip:hover .tooltip-text {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
#modifier-card-size-slider {
|
||||
width: 6em;
|
||||
margin-bottom: 0.5em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#modifier-settings-btn {
|
||||
float: right;
|
||||
}
|
||||
#modifier-settings-config textarea {
|
||||
width: 90%;
|
||||
height: 150px;
|
||||
}
|
161
ui/media/css/themes.css
Normal file
@ -0,0 +1,161 @@
|
||||
:root {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 4%;
|
||||
--value-base: 13%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.5 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1.5 * var(--value-step))));
|
||||
|
||||
--accent-hue: 267;
|
||||
--accent-lightness: 36%;
|
||||
--accent-lightness-hover: 40%;
|
||||
|
||||
--text-color: #eee;
|
||||
|
||||
--input-text-color: #eee;
|
||||
--input-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.7 * var(--value-step))));
|
||||
--input-border-color: var(--background-color4);
|
||||
|
||||
--button-text-color: var(--input-text-color);
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
/* other */
|
||||
--input-border-radius: 4px;
|
||||
--input-border-size: 1px;
|
||||
--accent-color: hsl(var(--accent-hue), 100%, var(--accent-lightness));
|
||||
--accent-color-hover: hsl(var(--accent-hue), 100%, var(--accent-lightness-hover));
|
||||
--primary-button-border: none;
|
||||
--input-switch-padding: 1px;
|
||||
--input-height: 18px;
|
||||
|
||||
/* Main theme color, hex color fallback. */
|
||||
--theme-color-fallback: #673AB6;
|
||||
}
|
||||
|
||||
.theme-light {
|
||||
--background-color1: white;
|
||||
--background-color2: #ececec;
|
||||
--background-color3: #e7e9eb;
|
||||
--background-color4: #cccccc;
|
||||
|
||||
--text-color: black;
|
||||
--button-text-color: white;
|
||||
|
||||
--input-text-color: black;
|
||||
--input-background-color: #f8f9fa;
|
||||
--input-border-color: grey;
|
||||
|
||||
--theme-color-fallback: #aaaaaa;
|
||||
}
|
||||
|
||||
.theme-discord {
|
||||
--background-color1: #36393f;
|
||||
--background-color2: #2f3136;
|
||||
--background-color3: #292b2f;
|
||||
--background-color4: #202225;
|
||||
|
||||
--accent-hue: 235;
|
||||
--accent-lightness: 65%;
|
||||
|
||||
--input-border-size: 2px;
|
||||
--input-background-color: #202225;
|
||||
--input-border-color: var(--input-background-color);
|
||||
|
||||
--theme-color-fallback: #202225;
|
||||
}
|
||||
|
||||
.theme-cool-blue {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 18%;
|
||||
--value-step: 3%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
|
||||
--accent-hue: 212;
|
||||
|
||||
--theme-color-fallback: #0056b8;
|
||||
}
|
||||
|
||||
|
||||
.theme-blurple {
|
||||
--main-hue: 235;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 16%;
|
||||
--value-step: 3%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
|
||||
--theme-color-fallback: #5300b8;
|
||||
}
|
||||
|
||||
.theme-super-dark {
|
||||
--main-hue: 222;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 5%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1.4 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
--input-border-size: 0px;
|
||||
|
||||
--theme-color-fallback: #000000;
|
||||
}
|
||||
|
||||
.theme-wild {
|
||||
--main-hue: 128;
|
||||
--main-saturation: 18%;
|
||||
--value-base: 20%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--accent-hue: 212;
|
||||
|
||||
--input-border-size: 1px;
|
||||
--input-background-color: hsl(222, var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--input-text-color: #FF0000;
|
||||
--input-border-color: #005E05;
|
||||
}
|
||||
|
||||
.theme-gnomie {
|
||||
--background-color1: #242424;
|
||||
--background-color2: #353535;
|
||||
--background-color3: #494949;
|
||||
--background-color4: #000000;
|
||||
|
||||
--accent-hue: 213;
|
||||
--accent-lightness: 55%;
|
||||
--accent-color: #2168bf;
|
||||
|
||||
--input-border-radius: 6px;
|
||||
--input-text-color: #ffffff;
|
||||
--input-background-color: #2a2a2a;
|
||||
--input-border-size: 0px;
|
||||
--input-border-color: var(--input-background-color);
|
||||
|
||||
--theme-color-fallback: #2168bf;
|
||||
}
|
||||
|
||||
.theme-gnomie .panel-box {
|
||||
border: none;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
BIN
ui/media/ding.mp3
Normal file
BIN
ui/media/fonts/fa-brands-400.ttf
Normal file
BIN
ui/media/fonts/fa-brands-400.woff2
Normal file
BIN
ui/media/fonts/fa-regular-400.ttf
Normal file
BIN
ui/media/fonts/fa-regular-400.woff2
Normal file
BIN
ui/media/fonts/fa-solid-900.ttf
Normal file
BIN
ui/media/fonts/fa-solid-900.woff2
Normal file
BIN
ui/media/fonts/fa-v4compatibility.ttf
Normal file
BIN
ui/media/fonts/fa-v4compatibility.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-600.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-600.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-700.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-700.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-800.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-800.woff2
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-regular.woff
Normal file
BIN
ui/media/fonts/work-sans-v18-latin-regular.woff2
Normal file
BIN
ui/media/images/favicon-16x16.png
Normal file
After Width: | Height: | Size: 466 B |
BIN
ui/media/images/favicon-32x32.png
Normal file
After Width: | Height: | Size: 973 B |
BIN
ui/media/images/kofi.png
Normal file
After Width: | Height: | Size: 11 KiB |
304
ui/media/js/auto-save.js
Normal file
@ -0,0 +1,304 @@
|
||||
// Saving settings
|
||||
let saveSettingsConfigTable = document.getElementById("save-settings-config-table")
|
||||
let saveSettingsConfigOverlay = document.getElementById("save-settings-config")
|
||||
let resetImageSettingsButton = document.getElementById("reset-image-settings")
|
||||
|
||||
const SETTINGS_KEY = "user_settings_v2"
|
||||
|
||||
const SETTINGS = {} // key=id. dict initialized in initSettings. { element, default, value, ignore }
|
||||
const SETTINGS_IDS_LIST = [
|
||||
"prompt",
|
||||
"seed",
|
||||
"random_seed",
|
||||
"num_outputs_total",
|
||||
"num_outputs_parallel",
|
||||
"stable_diffusion_model",
|
||||
"vae_model",
|
||||
"sampler",
|
||||
"width",
|
||||
"height",
|
||||
"num_inference_steps",
|
||||
"guidance_scale",
|
||||
"prompt_strength",
|
||||
"output_format",
|
||||
"negative_prompt",
|
||||
"stream_image_progress",
|
||||
"use_face_correction",
|
||||
"use_upscale",
|
||||
"show_only_filtered_image",
|
||||
"upscale_model",
|
||||
"preview-image",
|
||||
"modifier-card-size-slider",
|
||||
"theme",
|
||||
"save_to_disk",
|
||||
"diskPath",
|
||||
"sound_toggle",
|
||||
"turbo",
|
||||
"use_full_precision",
|
||||
"confirm_dangerous_actions",
|
||||
"auto_save_settings"
|
||||
]
|
||||
|
||||
const IGNORE_BY_DEFAULT = [
|
||||
"prompt"
|
||||
]
|
||||
|
||||
const SETTINGS_SECTIONS = [ // gets the "keys" property filled in with an ordered list of settings in this section via initSettings
|
||||
{ id: "editor-inputs", name: "Prompt" },
|
||||
{ id: "editor-settings", name: "Image Settings" },
|
||||
{ id: "system-settings", name: "System Settings" },
|
||||
{ id: "container", name: "Other" }
|
||||
]
|
||||
|
||||
async function initSettings() {
|
||||
SETTINGS_IDS_LIST.forEach(id => {
|
||||
var element = document.getElementById(id)
|
||||
if (!element) {
|
||||
console.error(`Missing settings element ${id}`)
|
||||
}
|
||||
if (id in SETTINGS) { // don't create it again
|
||||
return
|
||||
}
|
||||
SETTINGS[id] = {
|
||||
key: id,
|
||||
element: element,
|
||||
label: getSettingLabel(element),
|
||||
default: getSetting(element),
|
||||
value: getSetting(element),
|
||||
ignore: IGNORE_BY_DEFAULT.includes(id)
|
||||
}
|
||||
element.addEventListener("input", settingChangeHandler)
|
||||
element.addEventListener("change", settingChangeHandler)
|
||||
})
|
||||
var unsorted_settings_ids = [...SETTINGS_IDS_LIST]
|
||||
SETTINGS_SECTIONS.forEach(section => {
|
||||
var name = section.name
|
||||
var element = document.getElementById(section.id)
|
||||
var unsorted_ids = unsorted_settings_ids.map(id => `#${id}`).join(",")
|
||||
var children = unsorted_ids == "" ? [] : Array.from(element.querySelectorAll(unsorted_ids));
|
||||
section.keys = []
|
||||
children.forEach(e => {
|
||||
section.keys.push(e.id)
|
||||
})
|
||||
unsorted_settings_ids = unsorted_settings_ids.filter(id => children.find(e => e.id == id) == undefined)
|
||||
})
|
||||
loadSettings()
|
||||
}
|
||||
|
||||
function getSetting(element) {
|
||||
if (typeof element === "string" || element instanceof String) {
|
||||
element = SETTINGS[element].element
|
||||
}
|
||||
if (element.type == "checkbox") {
|
||||
return element.checked
|
||||
}
|
||||
return element.value
|
||||
}
|
||||
function setSetting(element, value) {
|
||||
if (typeof element === "string" || element instanceof String) {
|
||||
element = SETTINGS[element].element
|
||||
}
|
||||
SETTINGS[element.id].value = value
|
||||
if (getSetting(element) == value) {
|
||||
return // no setting necessary
|
||||
}
|
||||
if (element.type == "checkbox") {
|
||||
element.checked = value
|
||||
}
|
||||
else {
|
||||
element.value = value
|
||||
}
|
||||
element.dispatchEvent(new Event("input"))
|
||||
element.dispatchEvent(new Event("change"))
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
var saved_settings = Object.values(SETTINGS).map(setting => {
|
||||
return {
|
||||
key: setting.key,
|
||||
value: setting.value,
|
||||
ignore: setting.ignore
|
||||
}
|
||||
})
|
||||
localStorage.setItem(SETTINGS_KEY, JSON.stringify(saved_settings))
|
||||
}
|
||||
|
||||
var CURRENTLY_LOADING_SETTINGS = false
|
||||
function loadSettings() {
|
||||
var saved_settings_text = localStorage.getItem(SETTINGS_KEY)
|
||||
if (saved_settings_text) {
|
||||
var saved_settings = JSON.parse(saved_settings_text)
|
||||
if (saved_settings.find(s => s.key == "auto_save_settings").value == false) {
|
||||
setSetting("auto_save_settings", false)
|
||||
return
|
||||
}
|
||||
CURRENTLY_LOADING_SETTINGS = true
|
||||
saved_settings.forEach(saved_setting => {
|
||||
var setting = SETTINGS[saved_setting.key]
|
||||
if (!setting) {
|
||||
console.warn(`Attempted to load setting ${saved_setting.key}, but no setting found`);
|
||||
return null;
|
||||
}
|
||||
setting.ignore = saved_setting.ignore
|
||||
if (!setting.ignore) {
|
||||
setting.value = saved_setting.value
|
||||
setSetting(setting.element, setting.value)
|
||||
}
|
||||
})
|
||||
CURRENTLY_LOADING_SETTINGS = false
|
||||
}
|
||||
else {
|
||||
CURRENTLY_LOADING_SETTINGS = true
|
||||
tryLoadOldSettings();
|
||||
CURRENTLY_LOADING_SETTINGS = false
|
||||
saveSettings()
|
||||
}
|
||||
}
|
||||
|
||||
function loadDefaultSettingsSection(section_id) {
|
||||
CURRENTLY_LOADING_SETTINGS = true
|
||||
var section = SETTINGS_SECTIONS.find(s => s.id == section_id);
|
||||
section.keys.forEach(key => {
|
||||
var setting = SETTINGS[key];
|
||||
setting.value = setting.default
|
||||
setSetting(setting.element, setting.value)
|
||||
})
|
||||
CURRENTLY_LOADING_SETTINGS = false
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function settingChangeHandler(event) {
|
||||
if (!CURRENTLY_LOADING_SETTINGS) {
|
||||
var element = event.target
|
||||
var value = getSetting(element)
|
||||
if (value != SETTINGS[element.id].value) {
|
||||
SETTINGS[element.id].value = value
|
||||
saveSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSettingLabel(element) {
|
||||
var labelElement = document.querySelector(`label[for='${element.id}']`)
|
||||
var label = labelElement?.innerText || element.id
|
||||
var truncate_length = 30
|
||||
if (label.includes(" (")) {
|
||||
label = label.substring(0, label.indexOf(" ("))
|
||||
}
|
||||
if (label.length > truncate_length) {
|
||||
label = label.substring(0, truncate_length - 3) + "..."
|
||||
}
|
||||
label = label.replace("➕", "")
|
||||
label = label.replace("➖", "")
|
||||
return label
|
||||
}
|
||||
|
||||
function fillSaveSettingsConfigTable() {
|
||||
saveSettingsConfigTable.textContent = ""
|
||||
SETTINGS_SECTIONS.forEach(section => {
|
||||
var section_row = `<tr><th>${section.name}</th><td></td></tr>`
|
||||
saveSettingsConfigTable.insertAdjacentHTML("beforeend", section_row)
|
||||
section.keys.forEach(key => {
|
||||
var setting = SETTINGS[key]
|
||||
var element = setting.element
|
||||
var checkbox_id = `shouldsave_${element.id}`
|
||||
var is_checked = setting.ignore ? "" : "checked"
|
||||
var value = setting.value
|
||||
var value_truncate_length = 30
|
||||
if ((typeof value === "string" || value instanceof String) && value.length > value_truncate_length) {
|
||||
value = value.substring(0, value_truncate_length - 3) + "..."
|
||||
}
|
||||
var newrow = `<tr><td><label for="${checkbox_id}">${setting.label}</label></td><td><input id="${checkbox_id}" name="${checkbox_id}" ${is_checked} type="checkbox" ></td><td><small>(${value})</small></td></tr>`
|
||||
saveSettingsConfigTable.insertAdjacentHTML("beforeend", newrow)
|
||||
var checkbox = document.getElementById(checkbox_id)
|
||||
checkbox.addEventListener("input", event => {
|
||||
setting.ignore = !checkbox.checked
|
||||
saveSettings()
|
||||
})
|
||||
})
|
||||
})
|
||||
prettifyInputs(saveSettingsConfigTable)
|
||||
}
|
||||
|
||||
// configureSettingsSaveBtn
|
||||
|
||||
|
||||
|
||||
|
||||
var autoSaveSettings = document.getElementById("auto_save_settings")
|
||||
var configSettingsButton = document.createElement("button")
|
||||
configSettingsButton.textContent = "Configure"
|
||||
configSettingsButton.style.margin = "0px 5px"
|
||||
autoSaveSettings.insertAdjacentElement("beforebegin", configSettingsButton)
|
||||
autoSaveSettings.addEventListener("change", () => {
|
||||
configSettingsButton.style.display = autoSaveSettings.checked ? "block" : "none"
|
||||
})
|
||||
configSettingsButton.addEventListener('click', () => {
|
||||
fillSaveSettingsConfigTable()
|
||||
saveSettingsConfigOverlay.classList.add("active")
|
||||
})
|
||||
resetImageSettingsButton.addEventListener('click', event => {
|
||||
loadDefaultSettingsSection("editor-settings");
|
||||
event.stopPropagation()
|
||||
})
|
||||
|
||||
|
||||
function tryLoadOldSettings() {
|
||||
console.log("Loading old user settings")
|
||||
// load v1 auto-save.js settings
|
||||
var old_map = {
|
||||
"guidance_scale_slider": "guidance_scale",
|
||||
"prompt_strength_slider": "prompt_strength"
|
||||
}
|
||||
var settings_key_v1 = "user_settings"
|
||||
var saved_settings_text = localStorage.getItem(settings_key_v1)
|
||||
if (saved_settings_text) {
|
||||
var saved_settings = JSON.parse(saved_settings_text)
|
||||
Object.keys(saved_settings.should_save).forEach(key => {
|
||||
key = key in old_map ? old_map[key] : key
|
||||
SETTINGS[key].ignore = !saved_settings.should_save[key]
|
||||
});
|
||||
Object.keys(saved_settings.values).forEach(key => {
|
||||
key = key in old_map ? old_map[key] : key
|
||||
var setting = SETTINGS[key]
|
||||
if (!setting.ignore) {
|
||||
setting.value = saved_settings.values[key]
|
||||
setSetting(setting.element, setting.value)
|
||||
}
|
||||
});
|
||||
localStorage.removeItem(settings_key_v1)
|
||||
}
|
||||
|
||||
// load old individually stored items
|
||||
var individual_settings_map = { // maps old localStorage-key to new SETTINGS-key
|
||||
"soundEnabled": "sound_toggle",
|
||||
"saveToDisk": "save_to_disk",
|
||||
"useCPU": "use_cpu",
|
||||
"useFullPrecision": "use_full_precision",
|
||||
"useTurboMode": "turbo",
|
||||
"diskPath": "diskPath",
|
||||
"useFaceCorrection": "use_face_correction",
|
||||
"useUpscaling": "use_upscale",
|
||||
"showOnlyFilteredImage": "show_only_filtered_image",
|
||||
"streamImageProgress": "stream_image_progress",
|
||||
"outputFormat": "output_format",
|
||||
"autoSaveSettings": "auto_save_settings",
|
||||
};
|
||||
Object.keys(individual_settings_map).forEach(localStorageKey => {
|
||||
var localStorageValue = localStorage.getItem(localStorageKey);
|
||||
if (localStorageValue !== null) {
|
||||
let key = individual_settings_map[localStorageKey]
|
||||
var setting = SETTINGS[key]
|
||||
if (!setting) {
|
||||
console.warn(`Attempted to map old setting ${key}, but no setting found`);
|
||||
return null;
|
||||
}
|
||||
if (setting.element.type == "checkbox" && (typeof localStorageValue === "string" || localStorageValue instanceof String)) {
|
||||
localStorageValue = localStorageValue == "true"
|
||||
}
|
||||
setting.value = localStorageValue
|
||||
setSetting(setting.element, setting.value)
|
||||
localStorage.removeItem(localStorageKey);
|
||||
}
|
||||
})
|
||||
}
|
535
ui/media/js/dnd.js
Normal file
@ -0,0 +1,535 @@
|
||||
"use strict" // Opt in to a restricted variant of JavaScript
|
||||
|
||||
const EXT_REGEX = /(?:\.([^.]+))?$/
|
||||
const TEXT_EXTENSIONS = ['txt', 'json']
|
||||
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'tga']
|
||||
|
||||
function parseBoolean(stringValue) {
|
||||
if (typeof stringValue === 'boolean') {
|
||||
return stringValue
|
||||
}
|
||||
if (typeof stringValue === 'number') {
|
||||
return stringValue !== 0
|
||||
}
|
||||
if (typeof stringValue !== 'string') {
|
||||
return false
|
||||
}
|
||||
switch(stringValue?.toLowerCase()?.trim()) {
|
||||
case "true":
|
||||
case "yes":
|
||||
case "on":
|
||||
case "1":
|
||||
return true;
|
||||
|
||||
case "false":
|
||||
case "no":
|
||||
case "off":
|
||||
case "0":
|
||||
case null:
|
||||
case undefined:
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return Boolean(JSON.parse(stringValue));
|
||||
} catch {
|
||||
return Boolean(stringValue)
|
||||
}
|
||||
}
|
||||
|
||||
const TASK_MAPPING = {
|
||||
prompt: { name: 'Prompt',
|
||||
setUI: (prompt) => {
|
||||
promptField.value = prompt
|
||||
},
|
||||
readUI: () => promptField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
negative_prompt: { name: 'Negative Prompt',
|
||||
setUI: (negative_prompt) => {
|
||||
negativePromptField.value = negative_prompt
|
||||
},
|
||||
readUI: () => negativePromptField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
active_tags: { name: "Image Modifiers",
|
||||
setUI: (active_tags) => {
|
||||
refreshModifiersState(active_tags)
|
||||
},
|
||||
readUI: () => activeTags.map(x => x.name),
|
||||
parse: (val) => val
|
||||
},
|
||||
width: { name: 'Width',
|
||||
setUI: (width) => {
|
||||
const oldVal = widthField.value
|
||||
widthField.value = width
|
||||
if (!widthField.value) {
|
||||
widthField.value = oldVal
|
||||
}
|
||||
},
|
||||
readUI: () => parseInt(widthField.value),
|
||||
parse: (val) => parseInt(val)
|
||||
},
|
||||
height: { name: 'Height',
|
||||
setUI: (height) => {
|
||||
const oldVal = heightField.value
|
||||
heightField.value = height
|
||||
if (!heightField.value) {
|
||||
heightField.value = oldVal
|
||||
}
|
||||
},
|
||||
readUI: () => parseInt(heightField.value),
|
||||
parse: (val) => parseInt(val)
|
||||
},
|
||||
seed: { name: 'Seed',
|
||||
setUI: (seed) => {
|
||||
if (!seed) {
|
||||
randomSeedField.checked = true
|
||||
seedField.disabled = true
|
||||
return
|
||||
}
|
||||
randomSeedField.checked = false
|
||||
seedField.disabled = false
|
||||
seedField.value = seed
|
||||
},
|
||||
readUI: () => (randomSeedField.checked ? Math.floor(Math.random() * 10000000) : parseInt(seedField.value)),
|
||||
parse: (val) => parseInt(val)
|
||||
},
|
||||
num_inference_steps: { name: 'Steps',
|
||||
setUI: (num_inference_steps) => {
|
||||
numInferenceStepsField.value = num_inference_steps
|
||||
},
|
||||
readUI: () => parseInt(numInferenceStepsField.value),
|
||||
parse: (val) => parseInt(val)
|
||||
},
|
||||
guidance_scale: { name: 'Guidance Scale',
|
||||
setUI: (guidance_scale) => {
|
||||
guidanceScaleField.value = guidance_scale
|
||||
updateGuidanceScaleSlider()
|
||||
},
|
||||
readUI: () => parseFloat(guidanceScaleField.value),
|
||||
parse: (val) => parseFloat(val)
|
||||
},
|
||||
prompt_strength: { name: 'Prompt Strength',
|
||||
setUI: (prompt_strength) => {
|
||||
promptStrengthField.value = prompt_strength
|
||||
updatePromptStrengthSlider()
|
||||
},
|
||||
readUI: () => parseFloat(promptStrengthField.value),
|
||||
parse: (val) => parseFloat(val)
|
||||
},
|
||||
|
||||
init_image: { name: 'Initial Image',
|
||||
setUI: (init_image) => {
|
||||
initImagePreview.src = init_image
|
||||
},
|
||||
readUI: () => initImagePreview.src,
|
||||
parse: (val) => val
|
||||
},
|
||||
mask: { name: 'Mask',
|
||||
setUI: (mask) => {
|
||||
inpaintingEditor.setImg(mask)
|
||||
maskSetting.checked = Boolean(mask)
|
||||
},
|
||||
readUI: () => (maskSetting.checked ? inpaintingEditor.getImg() : undefined),
|
||||
parse: (val) => val
|
||||
},
|
||||
|
||||
use_face_correction: { name: 'Use Face Correction',
|
||||
setUI: (use_face_correction) => {
|
||||
useFaceCorrectionField.checked = parseBoolean(use_face_correction)
|
||||
},
|
||||
readUI: () => useFaceCorrectionField.checked,
|
||||
parse: (val) => parseBoolean(val)
|
||||
},
|
||||
use_upscale: { name: 'Use Upscaling',
|
||||
setUI: (use_upscale) => {
|
||||
const oldVal = upscaleModelField.value
|
||||
upscaleModelField.value = use_upscale
|
||||
if (upscaleModelField.value) { // Is a valid value for the field.
|
||||
useUpscalingField.checked = true
|
||||
upscaleModelField.disabled = false
|
||||
} else { // Not a valid value, restore the old value and disable the filter.
|
||||
upscaleModelField.disabled = true
|
||||
upscaleModelField.value = oldVal
|
||||
useUpscalingField.checked = false
|
||||
}
|
||||
},
|
||||
readUI: () => (useUpscalingField.checked ? upscaleModelField.value : undefined),
|
||||
parse: (val) => val
|
||||
},
|
||||
sampler: { name: 'Sampler',
|
||||
setUI: (sampler) => {
|
||||
samplerField.value = sampler
|
||||
},
|
||||
readUI: () => samplerField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
use_stable_diffusion_model: { name: 'Stable Diffusion model',
|
||||
setUI: (use_stable_diffusion_model) => {
|
||||
const oldVal = stableDiffusionModelField.value
|
||||
|
||||
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, ['.ckpt'])
|
||||
stableDiffusionModelField.value = use_stable_diffusion_model
|
||||
|
||||
if (!stableDiffusionModelField.value) {
|
||||
stableDiffusionModelField.value = oldVal
|
||||
}
|
||||
},
|
||||
readUI: () => stableDiffusionModelField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
use_vae_model: { name: 'VAE model',
|
||||
setUI: (use_vae_model) => {
|
||||
const oldVal = vaeModelField.value
|
||||
|
||||
if (use_vae_model !== '') {
|
||||
use_vae_model = getModelPath(use_vae_model, ['.vae.pt', '.ckpt'])
|
||||
use_vae_model = use_vae_model !== '' ? use_vae_model : oldVal
|
||||
}
|
||||
vaeModelField.value = use_vae_model
|
||||
},
|
||||
readUI: () => vaeModelField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
|
||||
num_outputs: { name: 'Parallel Images',
|
||||
setUI: (num_outputs) => {
|
||||
numOutputsParallelField.value = num_outputs
|
||||
},
|
||||
readUI: () => parseInt(numOutputsParallelField.value),
|
||||
parse: (val) => val
|
||||
},
|
||||
|
||||
use_cpu: { name: 'Use CPU',
|
||||
setUI: (use_cpu) => {
|
||||
useCPUField.checked = use_cpu
|
||||
},
|
||||
readUI: () => useCPUField.checked,
|
||||
parse: (val) => val
|
||||
},
|
||||
turbo: { name: 'Turbo',
|
||||
setUI: (turbo) => {
|
||||
turboField.checked = turbo
|
||||
},
|
||||
readUI: () => turboField.checked,
|
||||
parse: (val) => Boolean(val)
|
||||
},
|
||||
use_full_precision: { name: 'Use Full Precision',
|
||||
setUI: (use_full_precision) => {
|
||||
useFullPrecisionField.checked = use_full_precision
|
||||
},
|
||||
readUI: () => useFullPrecisionField.checked,
|
||||
parse: (val) => Boolean(val)
|
||||
},
|
||||
|
||||
stream_image_progress: { name: 'Stream Image Progress',
|
||||
setUI: (stream_image_progress) => {
|
||||
streamImageProgressField.checked = (parseInt(numOutputsTotalField.value) > 50 ? false : stream_image_progress)
|
||||
},
|
||||
readUI: () => streamImageProgressField.checked,
|
||||
parse: (val) => Boolean(val)
|
||||
},
|
||||
show_only_filtered_image: { name: 'Show only the corrected/upscaled image',
|
||||
setUI: (show_only_filtered_image) => {
|
||||
showOnlyFilteredImageField.checked = show_only_filtered_image
|
||||
},
|
||||
readUI: () => showOnlyFilteredImageField.checked,
|
||||
parse: (val) => Boolean(val)
|
||||
},
|
||||
output_format: { name: 'Output Format',
|
||||
setUI: (output_format) => {
|
||||
outputFormatField.value = output_format
|
||||
},
|
||||
readUI: () => outputFormatField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
save_to_disk_path: { name: 'Save to disk path',
|
||||
setUI: (save_to_disk_path) => {
|
||||
saveToDiskField.checked = Boolean(save_to_disk_path)
|
||||
diskPathField.value = save_to_disk_path
|
||||
},
|
||||
readUI: () => diskPathField.value,
|
||||
parse: (val) => val
|
||||
}
|
||||
}
|
||||
function restoreTaskToUI(task, fieldsToSkip) {
|
||||
fieldsToSkip = fieldsToSkip || []
|
||||
|
||||
if ('numOutputsTotal' in task) {
|
||||
numOutputsTotalField.value = task.numOutputsTotal
|
||||
}
|
||||
if ('seed' in task) {
|
||||
randomSeedField.checked = false
|
||||
seedField.value = task.seed
|
||||
}
|
||||
if (!('reqBody' in task)) {
|
||||
return
|
||||
}
|
||||
for (const key in TASK_MAPPING) {
|
||||
if (key in task.reqBody && !fieldsToSkip.includes(key)) {
|
||||
TASK_MAPPING[key].setUI(task.reqBody[key])
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original tag
|
||||
promptField.value = task.reqBody.original_prompt || task.reqBody.prompt
|
||||
|
||||
// properly reset checkboxes
|
||||
if (!('use_face_correction' in task.reqBody)) {
|
||||
useFaceCorrectionField.checked = false
|
||||
}
|
||||
if (!('use_upscale' in task.reqBody)) {
|
||||
useUpscalingField.checked = false
|
||||
}
|
||||
if (!('mask' in task.reqBody)) {
|
||||
maskSetting.checked = false
|
||||
}
|
||||
upscaleModelField.disabled = !useUpscalingField.checked
|
||||
|
||||
// Show the source picture if present
|
||||
initImagePreview.src = (task.reqBody.init_image == undefined ? '' : task.reqBody.init_image)
|
||||
if (IMAGE_REGEX.test(initImagePreview.src)) {
|
||||
Boolean(task.reqBody.mask) ? inpaintingEditor.setImg(task.reqBody.mask) : inpaintingEditor.resetBackground()
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'table-row'
|
||||
//samplerSelectionContainer.style.display = 'none'
|
||||
// maskSetting.checked = false
|
||||
inpaintingEditorContainer.style.display = maskSetting.checked ? 'block' : 'none'
|
||||
} else {
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
// inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
// maskSetting.style.display = 'none'
|
||||
}
|
||||
}
|
||||
function readUI() {
|
||||
const reqBody = {}
|
||||
for (const key in TASK_MAPPING) {
|
||||
reqBody[key] = TASK_MAPPING[key].readUI()
|
||||
}
|
||||
return {
|
||||
'numOutputsTotal': parseInt(numOutputsTotalField.value),
|
||||
'seed': TASK_MAPPING['seed'].readUI(),
|
||||
'reqBody': reqBody
|
||||
}
|
||||
}
|
||||
function getModelPath(filename, extensions)
|
||||
{
|
||||
let pathIdx = filename.lastIndexOf('/') // Linux, Mac paths
|
||||
if (pathIdx < 0) {
|
||||
pathIdx = filename.lastIndexOf('\\') // Windows paths.
|
||||
}
|
||||
if (pathIdx >= 0) {
|
||||
filename = filename.slice(pathIdx + 1)
|
||||
}
|
||||
extensions.forEach(ext => {
|
||||
if (filename.endsWith(ext)) {
|
||||
filename = filename.slice(0, filename.length - ext.length)
|
||||
}
|
||||
})
|
||||
return filename
|
||||
}
|
||||
|
||||
const TASK_TEXT_MAPPING = {
|
||||
width: 'Width',
|
||||
height: 'Height',
|
||||
seed: 'Seed',
|
||||
num_inference_steps: 'Steps',
|
||||
guidance_scale: 'Guidance Scale',
|
||||
prompt_strength: 'Prompt Strength',
|
||||
use_face_correction: 'Use Face Correction',
|
||||
use_upscale: 'Use Upscaling',
|
||||
sampler: 'Sampler',
|
||||
negative_prompt: 'Negative Prompt',
|
||||
use_stable_diffusion_model: 'Stable Diffusion model'
|
||||
}
|
||||
const afterPromptRe = /^\s*Width\s*:\s*\d+\s*(?:\r\n|\r|\n)+\s*Height\s*:\s*\d+\s*(\r\n|\r|\n)+Seed\s*:\s*\d+\s*$/igm
|
||||
function parseTaskFromText(str) {
|
||||
const taskReqBody = {}
|
||||
|
||||
// Prompt
|
||||
afterPromptRe.lastIndex = 0
|
||||
const match = afterPromptRe.exec(str)
|
||||
if (match) {
|
||||
let prompt = str.slice(0, match.index)
|
||||
str = str.slice(prompt.length)
|
||||
taskReqBody.prompt = prompt.trim()
|
||||
console.log('Prompt:', taskReqBody.prompt)
|
||||
}
|
||||
for (const key in TASK_TEXT_MAPPING) {
|
||||
const name = TASK_TEXT_MAPPING[key];
|
||||
let val = undefined
|
||||
|
||||
const reName = new RegExp(`${name}\\ *:\\ *(.*)(?:\\r\\n|\\r|\\n)*`, 'igm')
|
||||
const match = reName.exec(str);
|
||||
if (match) {
|
||||
str = str.slice(0, match.index) + str.slice(match.index + match[0].length)
|
||||
val = match[1]
|
||||
}
|
||||
if (val !== undefined) {
|
||||
taskReqBody[key] = TASK_MAPPING[key].parse(val.trim())
|
||||
console.log(TASK_MAPPING[key].name + ':', taskReqBody[key])
|
||||
if (!str) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(taskReqBody).length <= 0) {
|
||||
return undefined
|
||||
}
|
||||
const task = { reqBody: taskReqBody }
|
||||
if ('seed' in taskReqBody) {
|
||||
task.seed = taskReqBody.seed
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
async function parseContent(text) {
|
||||
text = text.trim();
|
||||
if (text.startsWith('{') && text.endsWith('}')) {
|
||||
try {
|
||||
const task = JSON.parse(text)
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} catch (e) {
|
||||
console.warn(`JSON text content couldn't be parsed.`, e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
// Normal txt file.
|
||||
const task = parseTaskFromText(text)
|
||||
if (task) {
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} else {
|
||||
console.warn(`Raw text content couldn't be parsed.`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function readFile(file, i) {
|
||||
console.log(`Event %o reading file[${i}]:${file.name}...`)
|
||||
const fileContent = (await file.text()).trim()
|
||||
return await parseContent(fileContent)
|
||||
}
|
||||
|
||||
function dropHandler(ev) {
|
||||
console.log('Content dropped...')
|
||||
let items = []
|
||||
|
||||
if (ev?.dataTransfer?.items) { // Use DataTransferItemList interface
|
||||
items = Array.from(ev.dataTransfer.items)
|
||||
items = items.filter(item => item.kind === 'file')
|
||||
items = items.map(item => item.getAsFile())
|
||||
} else if (ev?.dataTransfer?.files) { // Use DataTransfer interface
|
||||
items = Array.from(ev.dataTransfer.files)
|
||||
}
|
||||
|
||||
items.forEach(item => {item.file_ext = EXT_REGEX.exec(item.name.toLowerCase())[1]})
|
||||
|
||||
let text_items = items.filter(item => TEXT_EXTENSIONS.includes(item.file_ext))
|
||||
let image_items = items.filter(item => IMAGE_EXTENSIONS.includes(item.file_ext))
|
||||
|
||||
if (image_items.length > 0 && ev.target == initImageSelector) {
|
||||
return // let the event bubble up, so that the Init Image filepicker can receive this
|
||||
}
|
||||
|
||||
ev.preventDefault() // Prevent default behavior (Prevent file/content from being opened)
|
||||
text_items.forEach(readFile)
|
||||
}
|
||||
function dragOverHandler(ev) {
|
||||
console.log('Content in drop zone')
|
||||
|
||||
// Prevent default behavior (Prevent file/content from being opened)
|
||||
ev.preventDefault()
|
||||
|
||||
ev.dataTransfer.dropEffect = "copy"
|
||||
|
||||
let img = new Image()
|
||||
img.src = location.host + '/media/images/favicon-32x32.png'
|
||||
ev.dataTransfer.setDragImage(img, 16, 16)
|
||||
}
|
||||
|
||||
document.addEventListener("drop", dropHandler)
|
||||
document.addEventListener("dragover", dragOverHandler)
|
||||
|
||||
const TASK_REQ_NO_EXPORT = [
|
||||
"use_cpu",
|
||||
"turbo",
|
||||
"use_full_precision",
|
||||
"save_to_disk_path"
|
||||
]
|
||||
const resetSettings = document.getElementById('reset-image-settings')
|
||||
|
||||
function checkReadTextClipboardPermission (result) {
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// PASTE ICON
|
||||
const pasteIcon = document.createElement('i')
|
||||
pasteIcon.className = 'fa-solid fa-paste section-button'
|
||||
pasteIcon.innerHTML = `<span class="simple-tooltip right">Paste Image Settings</span>`
|
||||
pasteIcon.addEventListener('click', async (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
pasteIcon.classList.add('active')
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => pasteIcon.classList.remove('active'))
|
||||
|
||||
// Retrieve clipboard content and try to parse it
|
||||
const text = await navigator.clipboard.readText();
|
||||
await parseContent(text)
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||
}
|
||||
navigator.permissions.query({ name: "clipboard-read" }).then(checkReadTextClipboardPermission, (reason) => console.log('clipboard-read is not available. %o', reason))
|
||||
|
||||
document.addEventListener('paste', async (event) => {
|
||||
if (event.target) {
|
||||
const targetTag = event.target.tagName.toLowerCase()
|
||||
// Disable when targeting input elements.
|
||||
if (targetTag === 'input' || targetTag === 'textarea') {
|
||||
return
|
||||
}
|
||||
}
|
||||
const paste = (event.clipboardData || window.clipboardData).getData('text')
|
||||
const selection = window.getSelection()
|
||||
if (selection.toString().trim().length <= 0 && await parseContent(paste)) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
|
||||
function checkWriteToClipboardPermission (result) {
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// COPY ICON
|
||||
const copyIcon = document.createElement('i')
|
||||
copyIcon.className = 'fa-solid fa-clipboard section-button'
|
||||
copyIcon.innerHTML = `<span class="simple-tooltip right">Copy Image Settings</span>`
|
||||
copyIcon.addEventListener('click', (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
copyIcon.classList.add('active')
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => copyIcon.classList.remove('active'))
|
||||
const uiState = readUI()
|
||||
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
||||
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
||||
delete uiState.reqBody.init_image
|
||||
delete uiState.reqBody.prompt_strength
|
||||
}
|
||||
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
||||
}
|
||||
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
|
||||
navigator.permissions.query({ name: "clipboard-write" }).then(checkWriteToClipboardPermission, (e) => {
|
||||
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === 'function') {
|
||||
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
|
||||
checkWriteToClipboardPermission({state:"granted"})
|
||||
}
|
||||
})
|
4
ui/media/js/drawingboard.min.js
vendored
Normal file
349
ui/media/js/image-modifiers.js
Normal file
@ -0,0 +1,349 @@
|
||||
let activeTags = []
|
||||
let modifiers = []
|
||||
let customModifiersGroupElement = undefined
|
||||
|
||||
let editorModifierEntries = document.querySelector('#editor-modifiers-entries')
|
||||
let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list')
|
||||
let editorTagsContainer = document.querySelector('#editor-inputs-tags-container')
|
||||
let modifierCardSizeSlider = document.querySelector('#modifier-card-size-slider')
|
||||
let previewImageField = document.querySelector('#preview-image')
|
||||
let modifierSettingsBtn = document.querySelector('#modifier-settings-btn')
|
||||
let modifierSettingsOverlay = document.querySelector('#modifier-settings-config')
|
||||
let customModifiersTextBox = document.querySelector('#custom-modifiers-input')
|
||||
let customModifierEntriesToolbar = document.querySelector('#editor-modifiers-entries-toolbar')
|
||||
|
||||
const modifierThumbnailPath = 'media/modifier-thumbnails'
|
||||
const activeCardClass = 'modifier-card-active'
|
||||
const CUSTOM_MODIFIERS_KEY = "customModifiers"
|
||||
|
||||
function createModifierCard(name, previews) {
|
||||
const modifierCard = document.createElement('div')
|
||||
modifierCard.className = 'modifier-card'
|
||||
modifierCard.innerHTML = `
|
||||
<div class="modifier-card-overlay"></div>
|
||||
<div class="modifier-card-image-container">
|
||||
<div class="modifier-card-image-overlay">+</div>
|
||||
<p class="modifier-card-error-label"></p>
|
||||
<img onerror="this.remove()" alt="Modifier Image" class="modifier-card-image">
|
||||
</div>
|
||||
<div class="modifier-card-container">
|
||||
<div class="modifier-card-label"><p></p></div>
|
||||
</div>`
|
||||
|
||||
const image = modifierCard.querySelector('.modifier-card-image')
|
||||
const errorText = modifierCard.querySelector('.modifier-card-error-label')
|
||||
const label = modifierCard.querySelector('.modifier-card-label')
|
||||
|
||||
errorText.innerText = 'No Image'
|
||||
|
||||
if (typeof previews == 'object') {
|
||||
image.src = previews[0]; // portrait
|
||||
image.setAttribute('preview-type', 'portrait')
|
||||
} else {
|
||||
image.remove()
|
||||
}
|
||||
|
||||
const maxLabelLength = 30
|
||||
const nameWithoutBy = name.replace('by ', '')
|
||||
|
||||
if(nameWithoutBy.length <= maxLabelLength) {
|
||||
label.querySelector('p').innerText = nameWithoutBy
|
||||
} else {
|
||||
const tooltipText = document.createElement('span')
|
||||
tooltipText.className = 'tooltip-text'
|
||||
tooltipText.innerText = name
|
||||
|
||||
label.classList.add('tooltip')
|
||||
label.appendChild(tooltipText)
|
||||
|
||||
label.querySelector('p').innerText = nameWithoutBy.substring(0, maxLabelLength) + '...'
|
||||
}
|
||||
|
||||
return modifierCard
|
||||
}
|
||||
|
||||
function createModifierGroup(modifierGroup, initiallyExpanded) {
|
||||
const title = modifierGroup.category
|
||||
const modifiers = modifierGroup.modifiers
|
||||
|
||||
const titleEl = document.createElement('h5')
|
||||
titleEl.className = 'collapsible'
|
||||
titleEl.innerText = title
|
||||
|
||||
const modifiersEl = document.createElement('div')
|
||||
modifiersEl.classList.add('collapsible-content', 'editor-modifiers-leaf')
|
||||
|
||||
if (initiallyExpanded === true) {
|
||||
titleEl.className += ' active'
|
||||
}
|
||||
|
||||
modifiers.forEach(modObj => {
|
||||
const modifierName = modObj.modifier
|
||||
const modifierPreviews = modObj?.previews?.map(preview => `${modifierThumbnailPath}/${preview.path}`)
|
||||
|
||||
const modifierCard = createModifierCard(modifierName, modifierPreviews)
|
||||
|
||||
if(typeof modifierCard == 'object') {
|
||||
modifiersEl.appendChild(modifierCard)
|
||||
const trimmedName = trimModifiers(modifierName)
|
||||
|
||||
modifierCard.addEventListener('click', () => {
|
||||
if (activeTags.map(x => trimModifiers(x.name)).includes(trimmedName)) {
|
||||
// remove modifier from active array
|
||||
activeTags = activeTags.filter(x => trimModifiers(x.name) != trimmedName)
|
||||
toggleCardState(modifierCard, false)
|
||||
} else {
|
||||
// add modifier to active array
|
||||
activeTags.push({
|
||||
'name': modifierName,
|
||||
'element': modifierCard.cloneNode(true),
|
||||
'originElement': modifierCard,
|
||||
'previews': modifierPreviews
|
||||
})
|
||||
toggleCardState(modifierCard, true)
|
||||
}
|
||||
|
||||
refreshTagsList()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
let brk = document.createElement('br')
|
||||
brk.style.clear = 'both'
|
||||
modifiersEl.appendChild(brk)
|
||||
|
||||
let e = document.createElement('div')
|
||||
e.appendChild(titleEl)
|
||||
e.appendChild(modifiersEl)
|
||||
|
||||
editorModifierEntries.insertBefore(e, customModifierEntriesToolbar.nextSibling)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
function trimModifiers(tag) {
|
||||
return tag.replace(/^\(+|\)+$/g, '').replace(/^\[+|\]+$/g, '')
|
||||
}
|
||||
|
||||
async function loadModifiers() {
|
||||
try {
|
||||
let res = await fetch('/get/modifiers')
|
||||
if (res.status === 200) {
|
||||
res = await res.json()
|
||||
|
||||
modifiers = res; // update global variable
|
||||
|
||||
res.reverse()
|
||||
|
||||
res.forEach((modifierGroup, idx) => {
|
||||
createModifierGroup(modifierGroup, idx === res.length - 1)
|
||||
})
|
||||
|
||||
createCollapsibles(editorModifierEntries)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching modifiers', e)
|
||||
}
|
||||
|
||||
loadCustomModifiers()
|
||||
}
|
||||
|
||||
function refreshModifiersState(newTags) {
|
||||
// clear existing modifiers
|
||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
||||
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
||||
if (activeTags.map(x => x.name).includes(modifierName)) {
|
||||
modifierCard.classList.remove(activeCardClass)
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
}
|
||||
})
|
||||
activeTags = []
|
||||
|
||||
// set new modifiers
|
||||
newTags.forEach(tag => {
|
||||
let found = false
|
||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
||||
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
||||
if (tag == modifierName) {
|
||||
// add modifier to active array
|
||||
if (!activeTags.map(x => x.name).includes(tag)) { // only add each tag once even if several custom modifier cards share the same tag
|
||||
activeTags.push({
|
||||
'name': modifierName,
|
||||
'element': modifierCard.cloneNode(true),
|
||||
'originElement': modifierCard
|
||||
})
|
||||
}
|
||||
modifierCard.classList.add(activeCardClass)
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '-'
|
||||
found = true
|
||||
}
|
||||
})
|
||||
if (found == false) { // custom tag went missing, create one here
|
||||
let modifierCard = createModifierCard(tag, undefined) // create a modifier card for the missing tag, no image
|
||||
|
||||
modifierCard.addEventListener('click', () => {
|
||||
if (activeTags.map(x => x.name).includes(tag)) {
|
||||
// remove modifier from active array
|
||||
activeTags = activeTags.filter(x => x.name != tag)
|
||||
modifierCard.classList.remove(activeCardClass)
|
||||
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
}
|
||||
refreshTagsList()
|
||||
})
|
||||
|
||||
activeTags.push({
|
||||
'name': tag,
|
||||
'element': modifierCard,
|
||||
'originElement': undefined // no origin element for missing tags
|
||||
})
|
||||
}
|
||||
})
|
||||
refreshTagsList()
|
||||
}
|
||||
|
||||
function refreshTagsList() {
|
||||
editorModifierTagsList.innerHTML = ''
|
||||
|
||||
if (activeTags.length == 0) {
|
||||
editorTagsContainer.style.display = 'none'
|
||||
return
|
||||
} else {
|
||||
editorTagsContainer.style.display = 'block'
|
||||
}
|
||||
|
||||
activeTags.forEach((tag, index) => {
|
||||
tag.element.querySelector('.modifier-card-image-overlay').innerText = '-'
|
||||
tag.element.classList.add('modifier-card-tiny')
|
||||
|
||||
editorModifierTagsList.appendChild(tag.element)
|
||||
|
||||
tag.element.addEventListener('click', () => {
|
||||
let idx = activeTags.indexOf(tag)
|
||||
|
||||
if (idx !== -1 && activeTags[idx].originElement !== undefined) {
|
||||
toggleCardState(activeTags[idx].originElement, false)
|
||||
|
||||
activeTags.splice(idx, 1)
|
||||
refreshTagsList()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let brk = document.createElement('br')
|
||||
brk.style.clear = 'both'
|
||||
editorModifierTagsList.appendChild(brk)
|
||||
}
|
||||
|
||||
function toggleCardState(card, makeActive) {
|
||||
if (makeActive) {
|
||||
card.classList.add(activeCardClass)
|
||||
card.querySelector('.modifier-card-image-overlay').innerText = '-'
|
||||
} else {
|
||||
card.classList.remove(activeCardClass)
|
||||
card.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
}
|
||||
}
|
||||
|
||||
function changePreviewImages(val) {
|
||||
const previewImages = document.querySelectorAll('.modifier-card-image-container img')
|
||||
|
||||
let previewArr = []
|
||||
|
||||
modifiers.map(x => x.modifiers).forEach(x => previewArr.push(...x.map(m => m.previews)))
|
||||
|
||||
previewArr = previewArr.map(x => {
|
||||
let obj = {}
|
||||
|
||||
x.forEach(preview => {
|
||||
obj[preview.name] = preview.path
|
||||
})
|
||||
|
||||
return obj
|
||||
})
|
||||
|
||||
previewImages.forEach(previewImage => {
|
||||
const currentPreviewType = previewImage.getAttribute('preview-type')
|
||||
const relativePreviewPath = previewImage.src.split(modifierThumbnailPath + '/').pop()
|
||||
|
||||
const previews = previewArr.find(preview => relativePreviewPath == preview[currentPreviewType])
|
||||
|
||||
if(typeof previews == 'object') {
|
||||
let preview = null
|
||||
|
||||
if (val == 'portrait') {
|
||||
preview = previews.portrait
|
||||
}
|
||||
else if (val == 'landscape') {
|
||||
preview = previews.landscape
|
||||
}
|
||||
|
||||
if(preview != null) {
|
||||
previewImage.src = `${modifierThumbnailPath}/${preview}`
|
||||
previewImage.setAttribute('preview-type', val)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function resizeModifierCards(val) {
|
||||
const cardSizePrefix = 'modifier-card-size_'
|
||||
const modifierCardClass = 'modifier-card'
|
||||
|
||||
const modifierCards = document.querySelectorAll(`.${modifierCardClass}`)
|
||||
const cardSize = n => `${cardSizePrefix}${n}`
|
||||
|
||||
modifierCards.forEach(card => {
|
||||
// remove existing size classes
|
||||
const classes = card.className.split(' ').filter(c => !c.startsWith(cardSizePrefix))
|
||||
card.className = classes.join(' ').trim()
|
||||
|
||||
if(val != 0) {
|
||||
card.classList.add(cardSize(val))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value)
|
||||
previewImageField.onchange = () => changePreviewImages(previewImageField.value)
|
||||
|
||||
modifierSettingsBtn.addEventListener('click', function(e) {
|
||||
modifierSettingsOverlay.classList.add("active")
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
function saveCustomModifiers() {
|
||||
localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim())
|
||||
|
||||
loadCustomModifiers()
|
||||
}
|
||||
|
||||
function loadCustomModifiers() {
|
||||
let customModifiers = localStorage.getItem(CUSTOM_MODIFIERS_KEY, '')
|
||||
customModifiersTextBox.value = customModifiers
|
||||
|
||||
if (customModifiersGroupElement !== undefined) {
|
||||
customModifiersGroupElement.remove()
|
||||
}
|
||||
|
||||
if (customModifiers && customModifiers.trim() !== '') {
|
||||
customModifiers = customModifiers.split('\n')
|
||||
customModifiers = customModifiers.filter(m => m.trim() !== '')
|
||||
customModifiers = customModifiers.map(function(m) {
|
||||
return {
|
||||
"modifier": m
|
||||
}
|
||||
})
|
||||
|
||||
let customGroup = {
|
||||
'category': 'Custom Modifiers',
|
||||
'modifiers': customModifiers
|
||||
}
|
||||
|
||||
customModifiersGroupElement = createModifierGroup(customGroup, true)
|
||||
|
||||
createCollapsibles(customModifiersGroupElement)
|
||||
}
|
||||
}
|
||||
|
||||
customModifiersTextBox.addEventListener('change', saveCustomModifiers)
|
41
ui/media/js/inpainting-editor.js
Normal file
@ -0,0 +1,41 @@
|
||||
const INPAINTING_EDITOR_SIZE = 450
|
||||
|
||||
let inpaintingEditorContainer = document.querySelector('#inpaintingEditor')
|
||||
let inpaintingEditor = new DrawingBoard.Board('inpaintingEditor', {
|
||||
color: "#ffffff",
|
||||
background: false,
|
||||
size: 30,
|
||||
webStorage: false,
|
||||
controls: [{'DrawingMode': {'filler': false}}, 'Size', 'Navigation']
|
||||
})
|
||||
let inpaintingEditorCanvasBackground = document.querySelector('.drawing-board-canvas-wrapper')
|
||||
|
||||
function resizeInpaintingEditor(widthValue, heightValue) {
|
||||
if (widthValue === heightValue) {
|
||||
widthValue = INPAINTING_EDITOR_SIZE
|
||||
heightValue = INPAINTING_EDITOR_SIZE
|
||||
} else if (widthValue > heightValue) {
|
||||
heightValue = (heightValue / widthValue) * INPAINTING_EDITOR_SIZE
|
||||
widthValue = INPAINTING_EDITOR_SIZE
|
||||
} else {
|
||||
widthValue = (widthValue / heightValue) * INPAINTING_EDITOR_SIZE
|
||||
heightValue = INPAINTING_EDITOR_SIZE
|
||||
}
|
||||
if (inpaintingEditor.opts.aspectRatio === (widthValue / heightValue).toFixed(3)) {
|
||||
// Same ratio, don't reset the canvas.
|
||||
return
|
||||
}
|
||||
inpaintingEditor.opts.aspectRatio = (widthValue / heightValue).toFixed(3)
|
||||
|
||||
inpaintingEditorContainer.style.width = widthValue + 'px'
|
||||
inpaintingEditorContainer.style.height = heightValue + 'px'
|
||||
inpaintingEditor.opts.enlargeYourContainer = true
|
||||
|
||||
inpaintingEditor.opts.size = inpaintingEditor.ctx.lineWidth
|
||||
inpaintingEditor.resize()
|
||||
|
||||
inpaintingEditor.ctx.lineCap = "round"
|
||||
inpaintingEditor.ctx.lineJoin = "round"
|
||||
inpaintingEditor.ctx.lineWidth = inpaintingEditor.opts.size
|
||||
inpaintingEditor.setColor(inpaintingEditor.opts.color)
|
||||
}
|
2
ui/media/js/jquery-3.6.1.min.js
vendored
Normal file
10
ui/media/js/jquery-confirm.min.js
vendored
Normal file
1356
ui/media/js/main.js
Normal file
6
ui/media/js/marked.min.js
vendored
Normal file
440
ui/media/js/parameters.js
Normal file
@ -0,0 +1,440 @@
|
||||
/**
|
||||
* Enum of parameter types
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
*/
|
||||
var ParameterType = {
|
||||
checkbox: "checkbox",
|
||||
select: "select",
|
||||
select_multiple: "select_multiple",
|
||||
custom: "custom",
|
||||
};
|
||||
|
||||
/**
|
||||
* JSDoc style
|
||||
* @typedef {object} Parameter
|
||||
* @property {string} id
|
||||
* @property {ParameterType} type
|
||||
* @property {string} label
|
||||
* @property {?string} note
|
||||
* @property {number|boolean|string} default
|
||||
*/
|
||||
|
||||
|
||||
/** @type {Array.<Parameter>} */
|
||||
var PARAMETERS = [
|
||||
{
|
||||
id: "theme",
|
||||
type: ParameterType.select,
|
||||
label: "Theme",
|
||||
default: "theme-default",
|
||||
note: "customize the look and feel of the ui",
|
||||
options: [ // Note: options expanded dynamically
|
||||
{
|
||||
value: "theme-default",
|
||||
label: "Default"
|
||||
}
|
||||
],
|
||||
icon: "fa-palette"
|
||||
},
|
||||
{
|
||||
id: "save_to_disk",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Auto-Save Images",
|
||||
note: "automatically saves images to the specified location",
|
||||
icon: "fa-download",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "diskPath",
|
||||
type: ParameterType.custom,
|
||||
label: "Save Location",
|
||||
render: (parameter) => {
|
||||
return `<input id="${parameter.id}" name="${parameter.id}" size="30" disabled>`
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "sound_toggle",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Enable Sound",
|
||||
note: "plays a sound on task completion",
|
||||
icon: "fa-volume-low",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "ui_open_browser_on_start",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Open browser on startup",
|
||||
note: "starts the default browser on startup",
|
||||
icon: "fa-window-restore",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "turbo",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Turbo Mode",
|
||||
note: "generates images faster, but uses an additional 1 GB of GPU memory",
|
||||
icon: "fa-forward",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "use_cpu",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Use CPU (not GPU)",
|
||||
note: "warning: this will be *very* slow",
|
||||
icon: "fa-microchip",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "auto_pick_gpus",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Automatically pick the GPUs (experimental)",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "use_gpus",
|
||||
type: ParameterType.select_multiple,
|
||||
label: "GPUs to use (experimental)",
|
||||
note: "to process in parallel",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "use_full_precision",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Use Full Precision",
|
||||
note: "for GPU-only. warning: this will consume more VRAM",
|
||||
icon: "fa-crosshairs",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "auto_save_settings",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Auto-Save Settings",
|
||||
note: "restores settings on browser load",
|
||||
icon: "fa-gear",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "confirm_dangerous_actions",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Confirm dangerous actions",
|
||||
note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog",
|
||||
icon: "fa-check-double",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "listen_to_network",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Make Stable Diffusion available on your network",
|
||||
note: "Other devices on your network can access this web page",
|
||||
icon: "fa-network-wired",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "listen_port",
|
||||
type: ParameterType.custom,
|
||||
label: "Network port",
|
||||
note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'",
|
||||
icon: "fa-anchor",
|
||||
render: (parameter) => {
|
||||
return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "test_sd2",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Test SD 2.0",
|
||||
note: "Experimental! High memory usage! GPU-only! Not the final version! Please restart the program after changing this.",
|
||||
icon: "fa-fire",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: "use_beta_channel",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Beta channel",
|
||||
note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
|
||||
icon: "fa-fire",
|
||||
default: false,
|
||||
},
|
||||
];
|
||||
|
||||
function getParameterSettingsEntry(id) {
|
||||
let parameter = PARAMETERS.filter(p => p.id === id)
|
||||
if (parameter.length === 0) {
|
||||
return
|
||||
}
|
||||
return parameter[0].settingsEntry
|
||||
}
|
||||
|
||||
function getParameterElement(parameter) {
|
||||
switch (parameter.type) {
|
||||
case ParameterType.checkbox:
|
||||
var is_checked = parameter.default ? " checked" : "";
|
||||
return `<input id="${parameter.id}" name="${parameter.id}"${is_checked} type="checkbox">`
|
||||
case ParameterType.select:
|
||||
case ParameterType.select_multiple:
|
||||
var options = (parameter.options || []).map(option => `<option value="${option.value}">${option.label}</option>`).join("")
|
||||
var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '')
|
||||
return `<select id="${parameter.id}" name="${parameter.id}" ${multiple}>${options}</select>`
|
||||
case ParameterType.custom:
|
||||
return parameter.render(parameter)
|
||||
default:
|
||||
console.error(`Invalid type for parameter ${parameter.id}`);
|
||||
return "ERROR: Invalid Type"
|
||||
}
|
||||
}
|
||||
|
||||
let parametersTable = document.querySelector("#system-settings .parameters-table")
|
||||
/* fill in the system settings popup table */
|
||||
function initParameters() {
|
||||
PARAMETERS.forEach(parameter => {
|
||||
var element = getParameterElement(parameter)
|
||||
var note = parameter.note ? `<small>${parameter.note}</small>` : "";
|
||||
var icon = parameter.icon ? `<i class="fa ${parameter.icon}"></i>` : "";
|
||||
var newrow = document.createElement('div')
|
||||
newrow.innerHTML = `
|
||||
<div>${icon}</div>
|
||||
<div><label for="${parameter.id}">${parameter.label}</label>${note}</div>
|
||||
<div>${element}</div>`
|
||||
parametersTable.appendChild(newrow)
|
||||
parameter.settingsEntry = newrow
|
||||
})
|
||||
}
|
||||
|
||||
initParameters()
|
||||
|
||||
let turboField = document.querySelector('#turbo')
|
||||
let useCPUField = document.querySelector('#use_cpu')
|
||||
let autoPickGPUsField = document.querySelector('#auto_pick_gpus')
|
||||
let useGPUsField = document.querySelector('#use_gpus')
|
||||
let useFullPrecisionField = document.querySelector('#use_full_precision')
|
||||
let saveToDiskField = document.querySelector('#save_to_disk')
|
||||
let diskPathField = document.querySelector('#diskPath')
|
||||
let listenToNetworkField = document.querySelector("#listen_to_network")
|
||||
let listenPortField = document.querySelector("#listen_port")
|
||||
let testSD2Field = document.querySelector("#test_sd2")
|
||||
let useBetaChannelField = document.querySelector("#use_beta_channel")
|
||||
let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_start")
|
||||
let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions")
|
||||
|
||||
let saveSettingsBtn = document.querySelector('#save-system-settings-btn')
|
||||
|
||||
|
||||
async function changeAppConfig(configDelta) {
|
||||
try {
|
||||
let res = await fetch('/app_config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(configDelta)
|
||||
})
|
||||
res = await res.json()
|
||||
|
||||
console.log('set config status response', res)
|
||||
} catch (e) {
|
||||
console.log('set config status error', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getAppConfig() {
|
||||
try {
|
||||
let res = await fetch('/get/app_config')
|
||||
const config = await res.json()
|
||||
|
||||
if (config.update_branch === 'beta') {
|
||||
useBetaChannelField.checked = true
|
||||
document.querySelector("#updateBranchLabel").innerText = "(beta)"
|
||||
}
|
||||
if (config.ui && config.ui.open_browser_on_start === false) {
|
||||
uiOpenBrowserOnStartField.checked = false
|
||||
}
|
||||
if ('test_sd2' in config) {
|
||||
testSD2Field.checked = config['test_sd2']
|
||||
}
|
||||
|
||||
let testSD2SettingEntry = getParameterSettingsEntry('test_sd2')
|
||||
testSD2SettingEntry.style.display = (config.update_branch === 'beta' ? '' : 'none')
|
||||
if (config.net && config.net.listen_to_network === false) {
|
||||
listenToNetworkField.checked = false
|
||||
}
|
||||
if (config.net && config.net.listen_port !== undefined) {
|
||||
listenPortField.value = config.net.listen_port
|
||||
}
|
||||
|
||||
console.log('get config status response', config)
|
||||
} catch (e) {
|
||||
console.log('get config status error', e)
|
||||
}
|
||||
}
|
||||
|
||||
saveToDiskField.addEventListener('change', function(e) {
|
||||
diskPathField.disabled = !this.checked
|
||||
})
|
||||
|
||||
function getCurrentRenderDeviceSelection() {
|
||||
let selectedGPUs = $('#use_gpus').val()
|
||||
|
||||
if (useCPUField.checked && !autoPickGPUsField.checked) {
|
||||
return 'cpu'
|
||||
}
|
||||
if (autoPickGPUsField.checked || selectedGPUs.length == 0) {
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
return selectedGPUs.join(',')
|
||||
}
|
||||
|
||||
useCPUField.addEventListener('click', function() {
|
||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
||||
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
|
||||
if (this.checked) {
|
||||
gpuSettingEntry.style.display = 'none'
|
||||
autoPickGPUSettingEntry.style.display = 'none'
|
||||
autoPickGPUsField.setAttribute('data-old-value', autoPickGPUsField.checked)
|
||||
autoPickGPUsField.checked = false
|
||||
} else if (useGPUsField.options.length >= MIN_GPUS_TO_SHOW_SELECTION) {
|
||||
gpuSettingEntry.style.display = ''
|
||||
autoPickGPUSettingEntry.style.display = ''
|
||||
let oldVal = autoPickGPUsField.getAttribute('data-old-value')
|
||||
if (oldVal === null || oldVal === undefined) { // the UI started with CPU selected by default
|
||||
autoPickGPUsField.checked = true
|
||||
} else {
|
||||
autoPickGPUsField.checked = (oldVal === 'true')
|
||||
}
|
||||
gpuSettingEntry.style.display = (autoPickGPUsField.checked ? 'none' : '')
|
||||
}
|
||||
})
|
||||
|
||||
useGPUsField.addEventListener('click', function() {
|
||||
let selectedGPUs = $('#use_gpus').val()
|
||||
autoPickGPUsField.checked = (selectedGPUs.length === 0)
|
||||
})
|
||||
|
||||
autoPickGPUsField.addEventListener('click', function() {
|
||||
if (this.checked) {
|
||||
$('#use_gpus').val([])
|
||||
}
|
||||
|
||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
||||
gpuSettingEntry.style.display = (this.checked ? 'none' : '')
|
||||
})
|
||||
|
||||
async function getDiskPath() {
|
||||
try {
|
||||
var diskPath = getSetting("diskPath")
|
||||
if (diskPath == '' || diskPath == undefined || diskPath == "undefined") {
|
||||
let res = await fetch('/get/output_dir')
|
||||
if (res.status === 200) {
|
||||
res = await res.json()
|
||||
res = res.output_dir
|
||||
|
||||
setSetting("diskPath", res)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching output dir path', e)
|
||||
}
|
||||
}
|
||||
|
||||
function setDeviceInfo(devices) {
|
||||
let cpu = devices.all.cpu.name
|
||||
let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu')
|
||||
let activeGPUs = Object.keys(devices.active)
|
||||
|
||||
function ID_TO_TEXT(d) {
|
||||
let info = devices.all[d]
|
||||
if ("mem_free" in info && "mem_total" in info) {
|
||||
return `${info.name} <small>(${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total)</small>`
|
||||
} else {
|
||||
return `${info.name} <small>(${d}) (no memory info)</small>`
|
||||
}
|
||||
}
|
||||
|
||||
allGPUs = allGPUs.map(ID_TO_TEXT)
|
||||
activeGPUs = activeGPUs.map(ID_TO_TEXT)
|
||||
|
||||
let systemInfoEl = document.querySelector('#system-info')
|
||||
systemInfoEl.querySelector('#system-info-cpu').innerText = cpu
|
||||
systemInfoEl.querySelector('#system-info-gpus-all').innerHTML = allGPUs.join('</br>')
|
||||
systemInfoEl.querySelector('#system-info-rendering-devices').innerHTML = activeGPUs.join('</br>')
|
||||
}
|
||||
|
||||
function setHostInfo(hosts) {
|
||||
let port = listenPortField.value
|
||||
hosts = hosts.map(addr => `http://${addr}:${port}/`).map(url => `<div><a href="${url}">${url}</a></div>`)
|
||||
document.querySelector('#system-info-server-hosts').innerHTML = hosts.join('')
|
||||
}
|
||||
|
||||
async function getSystemInfo() {
|
||||
try {
|
||||
let res = await fetch('/get/system_info')
|
||||
if (res.status === 200) {
|
||||
res = await res.json()
|
||||
let devices = res['devices']
|
||||
let hosts = res['hosts']
|
||||
|
||||
let allDeviceIds = Object.keys(devices['all']).filter(d => d !== 'cpu')
|
||||
let activeDeviceIds = Object.keys(devices['active']).filter(d => d !== 'cpu')
|
||||
|
||||
if (activeDeviceIds.length === 0) {
|
||||
useCPUField.checked = true
|
||||
}
|
||||
|
||||
if (allDeviceIds.length < MIN_GPUS_TO_SHOW_SELECTION || useCPUField.checked) {
|
||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
||||
gpuSettingEntry.style.display = 'none'
|
||||
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
|
||||
autoPickGPUSettingEntry.style.display = 'none'
|
||||
}
|
||||
|
||||
if (allDeviceIds.length === 0) {
|
||||
useCPUField.checked = true
|
||||
useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory
|
||||
}
|
||||
|
||||
autoPickGPUsField.checked = (devices['config'] === 'auto')
|
||||
|
||||
useGPUsField.innerHTML = ''
|
||||
allDeviceIds.forEach(device => {
|
||||
let deviceName = devices['all'][device]['name']
|
||||
let deviceOption = `<option value="${device}">${deviceName} (${device})</option>`
|
||||
useGPUsField.insertAdjacentHTML('beforeend', deviceOption)
|
||||
})
|
||||
|
||||
if (autoPickGPUsField.checked) {
|
||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
||||
gpuSettingEntry.style.display = 'none'
|
||||
} else {
|
||||
$('#use_gpus').val(activeDeviceIds)
|
||||
}
|
||||
|
||||
setDeviceInfo(devices)
|
||||
setHostInfo(hosts)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching devices', e)
|
||||
}
|
||||
}
|
||||
|
||||
saveSettingsBtn.addEventListener('click', function() {
|
||||
let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main')
|
||||
|
||||
if (listenPortField.value == '') {
|
||||
alert('The network port field must not be empty.')
|
||||
} else if (listenPortField.value<1 || listenPortField.value>65535) {
|
||||
alert('The network port must be a number from 1 to 65535')
|
||||
} else {
|
||||
changeAppConfig({
|
||||
'render_devices': getCurrentRenderDeviceSelection(),
|
||||
'update_branch': updateBranch,
|
||||
'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked,
|
||||
'listen_to_network': listenToNetworkField.checked,
|
||||
'listen_port': listenPortField.value,
|
||||
'test_sd2': testSD2Field.checked
|
||||
})
|
||||
}
|
||||
|
||||
saveSettingsBtn.classList.add('active')
|
||||
asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active'))
|
||||
})
|
47
ui/media/js/plugins.js
Normal file
@ -0,0 +1,47 @@
|
||||
const PLUGIN_API_VERSION = "1.0"
|
||||
|
||||
const PLUGINS = {
|
||||
/**
|
||||
* Register new buttons to show on each output image.
|
||||
*
|
||||
* Example:
|
||||
* PLUGINS['IMAGE_INFO_BUTTONS'].push({
|
||||
* text: 'Make a Similar Image',
|
||||
* on_click: function(origRequest, image) {
|
||||
* let newTaskRequest = getCurrentUserRequest()
|
||||
* newTaskRequest.reqBody = Object.assign({}, origRequest, {
|
||||
* init_image: image.src,
|
||||
* prompt_strength: 0.7,
|
||||
* seed: Math.floor(Math.random() * 10000000)
|
||||
* })
|
||||
* newTaskRequest.seed = newTaskRequest.reqBody.seed
|
||||
* createTask(newTaskRequest)
|
||||
* },
|
||||
* filter: function(origRequest, image) {
|
||||
* // this is an optional function. return true/false to show/hide the button
|
||||
* // if this function isn't set, the button will always be visible
|
||||
* return true
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
IMAGE_INFO_BUTTONS: []
|
||||
}
|
||||
|
||||
async function loadUIPlugins() {
|
||||
try {
|
||||
let res = await fetch('/get/ui_plugins')
|
||||
if (res.status === 200) {
|
||||
res = await res.json()
|
||||
res.forEach(pluginPath => {
|
||||
let script = document.createElement('script')
|
||||
script.src = pluginPath + '?t=' + Date.now()
|
||||
|
||||
console.log('loading plugin', pluginPath)
|
||||
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching plugin paths', e)
|
||||
}
|
||||
}
|
81
ui/media/js/themes.js
Normal file
@ -0,0 +1,81 @@
|
||||
const themeField = document.getElementById("theme");
|
||||
var DEFAULT_THEME = {};
|
||||
var THEMES = []; // initialized in initTheme from data in css
|
||||
|
||||
function getThemeName(theme) {
|
||||
theme = theme.replace("theme-", "");
|
||||
theme = theme.split("-").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
||||
return theme;
|
||||
}
|
||||
// init themefield
|
||||
function initTheme() {
|
||||
Array.from(document.styleSheets)
|
||||
.filter(sheet => sheet.href?.startsWith(window.location.origin))
|
||||
.flatMap(sheet => Array.from(sheet.cssRules))
|
||||
.forEach(rule => {
|
||||
var selector = rule.selectorText; // TODO: also do selector == ":root", re-run un-set props
|
||||
if (selector && selector.startsWith(".theme-") && !selector.includes(" ")) {
|
||||
var theme_key = selector.substring(1);
|
||||
THEMES.push({
|
||||
key: theme_key,
|
||||
name: getThemeName(theme_key),
|
||||
rule: rule
|
||||
})
|
||||
}
|
||||
if (selector && selector == ":root") {
|
||||
DEFAULT_THEME = {
|
||||
key: "theme-default",
|
||||
name: "Default",
|
||||
rule: rule
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
THEMES.forEach(theme => {
|
||||
var new_option = document.createElement("option");
|
||||
new_option.setAttribute("value", theme.key);
|
||||
new_option.innerText = theme.name;
|
||||
themeField.appendChild(new_option);
|
||||
});
|
||||
|
||||
|
||||
// setup the style transitions a second after app initializes, so initial style is instant
|
||||
setTimeout(() => {
|
||||
var body = document.querySelector("body");
|
||||
var style = document.createElement('style');
|
||||
style.innerHTML = "* { transition: background 0.5s, color 0.5s, background-color 0.5s; }";
|
||||
body.appendChild(style);
|
||||
}, 1000);
|
||||
}
|
||||
initTheme();
|
||||
|
||||
function themeFieldChanged() {
|
||||
var theme_key = themeField.value;
|
||||
|
||||
var body = document.querySelector("body");
|
||||
body.classList.remove(...THEMES.map(theme => theme.key));
|
||||
body.classList.add(theme_key);
|
||||
|
||||
//
|
||||
|
||||
body.style = "";
|
||||
var theme = THEMES.find(t => t.key == theme_key);
|
||||
let borderColor = undefined
|
||||
if (theme) {
|
||||
// refresh variables incase they are back referencing
|
||||
Array.from(DEFAULT_THEME.rule.style)
|
||||
.filter(cssVariable => !Array.from(theme.rule.style).includes(cssVariable))
|
||||
.forEach(cssVariable => {
|
||||
body.style.setProperty(cssVariable, DEFAULT_THEME.rule.style.getPropertyValue(cssVariable));
|
||||
});
|
||||
borderColor = theme.rule.style.getPropertyValue('--input-border-color').trim()
|
||||
if (!borderColor.startsWith('#')) {
|
||||
borderColor = theme.rule.style.getPropertyValue('--theme-color-fallback')
|
||||
}
|
||||
} else {
|
||||
borderColor = DEFAULT_THEME.rule.style.getPropertyValue('--theme-color-fallback')
|
||||
}
|
||||
document.querySelector('meta[name="theme-color"]').setAttribute("content", borderColor)
|
||||
}
|
||||
|
||||
themeField.addEventListener('change', themeFieldChanged);
|
376
ui/media/js/utils.js
Normal file
@ -0,0 +1,376 @@
|
||||
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
|
||||
function getNextSibling(elem, selector) {
|
||||
// Get the next sibling element
|
||||
var sibling = elem.nextElementSibling
|
||||
|
||||
// If there's no selector, return the first sibling
|
||||
if (!selector) return sibling
|
||||
|
||||
// If the sibling matches our selector, use it
|
||||
// If not, jump to the next sibling and continue the loop
|
||||
while (sibling) {
|
||||
if (sibling.matches(selector)) return sibling
|
||||
sibling = sibling.nextElementSibling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Panel Stuff */
|
||||
|
||||
// true = open
|
||||
var COLLAPSIBLES_INITIALIZED = false;
|
||||
const COLLAPSIBLES_KEY = "collapsibles";
|
||||
const COLLAPSIBLE_PANELS = []; // filled in by createCollapsibles with all the elements matching .collapsible
|
||||
|
||||
// on-init call this for any panels that are marked open
|
||||
function toggleCollapsible(element) {
|
||||
var collapsibleHeader = element.querySelector(".collapsible");
|
||||
var handle = element.querySelector(".collapsible-handle");
|
||||
collapsibleHeader.classList.toggle("active")
|
||||
let content = getNextSibling(collapsibleHeader, '.collapsible-content')
|
||||
if (!collapsibleHeader.classList.contains("active")) {
|
||||
content.style.display = "none"
|
||||
if (handle != null) { // render results don't have a handle
|
||||
handle.innerHTML = '➕' // plus
|
||||
}
|
||||
} else {
|
||||
content.style.display = "block"
|
||||
if (handle != null) { // render results don't have a handle
|
||||
handle.innerHTML = '➖' // minus
|
||||
}
|
||||
}
|
||||
|
||||
if (COLLAPSIBLES_INITIALIZED && COLLAPSIBLE_PANELS.includes(element)) {
|
||||
saveCollapsibles()
|
||||
}
|
||||
}
|
||||
|
||||
function saveCollapsibles() {
|
||||
var values = {}
|
||||
COLLAPSIBLE_PANELS.forEach(element => {
|
||||
var value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
||||
values[element.id] = value
|
||||
})
|
||||
localStorage.setItem(COLLAPSIBLES_KEY, JSON.stringify(values))
|
||||
}
|
||||
|
||||
function createCollapsibles(node) {
|
||||
var save = false
|
||||
if (!node) {
|
||||
node = document
|
||||
save = true
|
||||
}
|
||||
let collapsibles = node.querySelectorAll(".collapsible")
|
||||
collapsibles.forEach(function(c) {
|
||||
if (save && c.parentElement.id) {
|
||||
COLLAPSIBLE_PANELS.push(c.parentElement)
|
||||
}
|
||||
let handle = document.createElement('span')
|
||||
handle.className = 'collapsible-handle'
|
||||
|
||||
if (c.classList.contains("active")) {
|
||||
handle.innerHTML = '➖' // minus
|
||||
} else {
|
||||
handle.innerHTML = '➕' // plus
|
||||
}
|
||||
c.insertBefore(handle, c.firstChild)
|
||||
|
||||
c.addEventListener('click', function() {
|
||||
toggleCollapsible(c.parentElement)
|
||||
})
|
||||
})
|
||||
if (save) {
|
||||
var saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
||||
if (!saved) {
|
||||
saved = tryLoadOldCollapsibles();
|
||||
}
|
||||
if (!saved) {
|
||||
saveCollapsibles()
|
||||
saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
||||
}
|
||||
var values = JSON.parse(saved)
|
||||
COLLAPSIBLE_PANELS.forEach(element => {
|
||||
var value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
||||
if (values[element.id] != value) {
|
||||
toggleCollapsible(element)
|
||||
}
|
||||
})
|
||||
COLLAPSIBLES_INITIALIZED = true
|
||||
}
|
||||
}
|
||||
|
||||
function tryLoadOldCollapsibles() {
|
||||
var old_map = {
|
||||
"advancedPanelOpen": "editor-settings",
|
||||
"modifiersPanelOpen": "editor-modifiers",
|
||||
"negativePromptPanelOpen": "editor-inputs-prompt"
|
||||
};
|
||||
if (localStorage.getItem(Object.keys(old_map)[0])) {
|
||||
var result = {};
|
||||
Object.keys(old_map).forEach(key => {
|
||||
var value = localStorage.getItem(key);
|
||||
if (value !== null) {
|
||||
result[old_map[key]] = value == true || value == "true"
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
});
|
||||
result = JSON.stringify(result)
|
||||
localStorage.setItem(COLLAPSIBLES_KEY, result)
|
||||
return result
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function permute(arr) {
|
||||
let permutations = []
|
||||
let n = arr.length
|
||||
let n_permutations = Math.pow(2, n)
|
||||
for (let i = 0; i < n_permutations; i++) {
|
||||
let perm = []
|
||||
let mask = Number(i).toString(2).padStart(n, '0')
|
||||
|
||||
for (let idx = 0; idx < mask.length; idx++) {
|
||||
if (mask[idx] === '1' && arr[idx].trim() !== '') {
|
||||
perm.push(arr[idx])
|
||||
}
|
||||
}
|
||||
|
||||
if (perm.length > 0) {
|
||||
permutations.push(perm)
|
||||
}
|
||||
}
|
||||
|
||||
return permutations
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/8212878
|
||||
function millisecondsToStr(milliseconds) {
|
||||
function numberEnding (number) {
|
||||
return (number > 1) ? 's' : ''
|
||||
}
|
||||
|
||||
var temp = Math.floor(milliseconds / 1000)
|
||||
var hours = Math.floor((temp %= 86400) / 3600)
|
||||
var s = ''
|
||||
if (hours) {
|
||||
s += hours + ' hour' + numberEnding(hours) + ' '
|
||||
}
|
||||
var minutes = Math.floor((temp %= 3600) / 60)
|
||||
if (minutes) {
|
||||
s += minutes + ' minute' + numberEnding(minutes) + ' '
|
||||
}
|
||||
var seconds = temp % 60
|
||||
if (!hours && minutes < 4 && seconds) {
|
||||
s += seconds + ' second' + numberEnding(seconds)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// https://rosettacode.org/wiki/Brace_expansion#JavaScript
|
||||
function BraceExpander() {
|
||||
'use strict'
|
||||
|
||||
// Index of any closing brace matching the opening
|
||||
// brace at iPosn,
|
||||
// with the indices of any immediately-enclosed commas.
|
||||
function bracePair(tkns, iPosn, iNest, lstCommas) {
|
||||
if (iPosn >= tkns.length || iPosn < 0) return null;
|
||||
|
||||
var t = tkns[iPosn],
|
||||
n = (t === '{') ? (
|
||||
iNest + 1
|
||||
) : (t === '}' ? (
|
||||
iNest - 1
|
||||
) : iNest),
|
||||
lst = (t === ',' && iNest === 1) ? (
|
||||
lstCommas.concat(iPosn)
|
||||
) : lstCommas;
|
||||
|
||||
return n ? bracePair(tkns, iPosn + 1, n, lst) : {
|
||||
close: iPosn,
|
||||
commas: lst
|
||||
};
|
||||
}
|
||||
|
||||
// Parse of a SYNTAGM subtree
|
||||
function andTree(dctSofar, tkns) {
|
||||
if (!tkns.length) return [dctSofar, []];
|
||||
|
||||
var dctParse = dctSofar ? dctSofar : {
|
||||
fn: and,
|
||||
args: []
|
||||
},
|
||||
|
||||
head = tkns[0],
|
||||
tail = head ? tkns.slice(1) : [],
|
||||
|
||||
dctBrace = head === '{' ? bracePair(
|
||||
tkns, 0, 0, []
|
||||
) : null,
|
||||
|
||||
lstOR = dctBrace && (
|
||||
dctBrace.close
|
||||
) && dctBrace.commas.length ? (
|
||||
splitAt(dctBrace.close + 1, tkns)
|
||||
) : null;
|
||||
|
||||
return andTree({
|
||||
fn: and,
|
||||
args: dctParse.args.concat(
|
||||
lstOR ? (
|
||||
orTree(dctParse, lstOR[0], dctBrace.commas)
|
||||
) : head
|
||||
)
|
||||
}, lstOR ? (
|
||||
lstOR[1]
|
||||
) : tail);
|
||||
}
|
||||
|
||||
// Parse of a PARADIGM subtree
|
||||
function orTree(dctSofar, tkns, lstCommas) {
|
||||
if (!tkns.length) return [dctSofar, []];
|
||||
var iLast = lstCommas.length;
|
||||
|
||||
return {
|
||||
fn: or,
|
||||
args: splitsAt(
|
||||
lstCommas, tkns
|
||||
).map(function (x, i) {
|
||||
var ts = x.slice(
|
||||
1, i === iLast ? (
|
||||
-1
|
||||
) : void 0
|
||||
);
|
||||
|
||||
return ts.length ? ts : [''];
|
||||
}).map(function (ts) {
|
||||
return ts.length > 1 ? (
|
||||
andTree(null, ts)[0]
|
||||
) : ts[0];
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// List of unescaped braces and commas, and remaining strings
|
||||
function tokens(str) {
|
||||
// Filter function excludes empty splitting artefacts
|
||||
var toS = function (x) {
|
||||
return x.toString();
|
||||
};
|
||||
|
||||
return str.split(/(\\\\)/).filter(toS).reduce(function (a, s) {
|
||||
return a.concat(s.charAt(0) === '\\' ? s : s.split(
|
||||
/(\\*[{,}])/
|
||||
).filter(toS));
|
||||
}, []);
|
||||
}
|
||||
|
||||
// PARSE TREE OPERATOR (1 of 2)
|
||||
// Each possible head * each possible tail
|
||||
function and(args) {
|
||||
var lng = args.length,
|
||||
head = lng ? args[0] : null,
|
||||
lstHead = "string" === typeof head ? (
|
||||
[head]
|
||||
) : head;
|
||||
|
||||
return lng ? (
|
||||
1 < lng ? lstHead.reduce(function (a, h) {
|
||||
return a.concat(
|
||||
and(args.slice(1)).map(function (t) {
|
||||
return h + t;
|
||||
})
|
||||
);
|
||||
}, []) : lstHead
|
||||
) : [];
|
||||
}
|
||||
|
||||
// PARSE TREE OPERATOR (2 of 2)
|
||||
// Each option flattened
|
||||
function or(args) {
|
||||
return args.reduce(function (a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
}
|
||||
|
||||
// One list split into two (first sublist length n)
|
||||
function splitAt(n, lst) {
|
||||
return n < lst.length + 1 ? [
|
||||
lst.slice(0, n), lst.slice(n)
|
||||
] : [lst, []];
|
||||
}
|
||||
|
||||
// One list split into several (sublist lengths [n])
|
||||
function splitsAt(lstN, lst) {
|
||||
return lstN.reduceRight(function (a, x) {
|
||||
return splitAt(x, a[0]).concat(a.slice(1));
|
||||
}, [lst]);
|
||||
}
|
||||
|
||||
// Value of the parse tree
|
||||
function evaluated(e) {
|
||||
return typeof e === 'string' ? e :
|
||||
e.fn(e.args.map(evaluated));
|
||||
}
|
||||
|
||||
// JSON prettyprint (for parse tree, token list etc)
|
||||
function pp(e) {
|
||||
return JSON.stringify(e, function (k, v) {
|
||||
return typeof v === 'function' ? (
|
||||
'[function ' + v.name + ']'
|
||||
) : v;
|
||||
}, 2)
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- MAIN ------------------------
|
||||
|
||||
// s -> [s]
|
||||
this.expand = function(s) {
|
||||
// BRACE EXPRESSION PARSED
|
||||
var dctParse = andTree(null, tokens(s))[0];
|
||||
|
||||
// ABSTRACT SYNTAX TREE LOGGED
|
||||
// console.log(pp(dctParse));
|
||||
|
||||
// AST EVALUATED TO LIST OF STRINGS
|
||||
return evaluated(dctParse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function asyncDelay(timeout) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
setTimeout(resolve, timeout, true)
|
||||
})
|
||||
}
|
||||
|
||||
function preventNonNumericalInput(e) {
|
||||
e = e || window.event;
|
||||
let charCode = (typeof e.which == "undefined") ? e.keyCode : e.which;
|
||||
let charStr = String.fromCharCode(charCode);
|
||||
let re = e.target.getAttribute('pattern') || '^[0-9]+$'
|
||||
re = new RegExp(re)
|
||||
|
||||
if (!charStr.match(re)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/* inserts custom html to allow prettifying of inputs */
|
||||
function prettifyInputs(root_element) {
|
||||
root_element.querySelectorAll(`input[type="checkbox"]`).forEach(element => {
|
||||
var parent = element.parentNode;
|
||||
if (!parent.classList.contains("input-toggle")) {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.classList.add("input-toggle");
|
||||
parent.replaceChild(wrapper, element);
|
||||
wrapper.appendChild(element);
|
||||
var label = document.createElement("label");
|
||||
label.htmlFor = element.id;
|
||||
wrapper.appendChild(label);
|
||||
}
|
||||
})
|
||||
}
|
8
ui/media/manifest.webmanifest
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Stable Diffusion UI",
|
||||
"display": "standalone",
|
||||
"display_override": [
|
||||
"window-controls-overlay"
|
||||
],
|
||||
"theme_color": "#000000"
|
||||
}
|
BIN
ui/media/modifier-thumbnails/artist/artstation/landscape-0.jpg
Normal file
After Width: | Height: | Size: 38 KiB |