Skip to content

Commit 7401abf

Browse files
[Installer] Dynamic theme preview loading, Iframe previews, and other small improvements (#1036)
* Dynamic theme preview loading, Iframe previews, and other improvements * oops * Fixes - Remove notice in install hint - Remove lazy-loading of iframes - Load CSS in iframe with C# instead of JS * Fix theme previews not being applied * Use ViewData instead of GET for overriding theme in _Layout * Add core parameters to preview Known issue: two icons are present in the seed buttons in modern themes * Fixes - Add missing semicolon in themepreview CSS - Remove unnecessary popover creation logic - Fix label CSS in installer CSS * Add noise image bg to core params * css debork --------- Co-authored-by: Alex "mcmonkey" Goodwin <git_commits@alexgoodwin.dev>
1 parent 51c1f65 commit 7401abf

File tree

5 files changed

+135
-220
lines changed

5 files changed

+135
-220
lines changed

src/Pages/Install.cshtml

Lines changed: 11 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -143,76 +143,17 @@
143143
<div class="install_hint">You can always change this later in the User Settings page.</div>
144144
<br>
145145
<fieldset class="form-group theme_preview" id="theme_selection_field">
146-
<div class="form-check theme_preview_modern_dark" id="theme_input_modern_dark">
147-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_moderndark" value="modern_dark" checked="">
148-
<label class="form-check-label" for="theme_moderndark">Modern Dark</label>
149-
<span class="button_example">Example</span>
150-
</div>
151-
<div class="form-check theme_preview_modern_light" id="theme_input_modern_light">
152-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_modernlight" value="modern_light">
153-
<label class="form-check-label" for="theme_modernlight">Modern Light</label>
154-
<span class="button_example">Example</span>
155-
</div>
156-
<div class="form-check theme_preview_solarized" id="theme_input_solarized">
157-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_solarized" value="solarized">
158-
<label class="form-check-label" for="theme_solarized">Solarized Light</label>
159-
<span class="button_example">Example</span>
160-
</div>
161-
<div class="form-check theme_preview_dark_dreams" id="theme_input_dark_dreams">
162-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_darkdreams" value="dark_dreams">
163-
<label class="form-check-label" for="theme_darkdreams">Dark Dreams</label>
164-
<span class="button_example">Example</span>
165-
</div>
166-
<div class="form-check theme_preview_gravity_blue" id="theme_input_gravity_blue">
167-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_gravityblue" value="gravity_blue">
168-
<label class="form-check-label" for="theme_gravityblue">Gravity Blue</label>
169-
<span class="button_example">Example</span>
170-
</div>
171-
<div class="form-check theme_preview_cyber_swarm" id="theme_input_cyber_swarm">
172-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_cyberswarm" value="cyber_swarm">
173-
<label class="form-check-label" for="theme_cyberswarm">Cyber Swarm</label>
174-
<span class="button_example">Example</span>
175-
</div>
176-
<div class="form-check theme_preview_punked" id="theme_input_punked">
177-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_punked" value="punked">
178-
<label class="form-check-label" for="theme_punked">Punked</label>
179-
<span class="button_example">Example</span>
180-
</div>
181-
<div class="form-check theme_preview_eyesear_white" id="theme_input_eyesear_white">
182-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_eyesearwhite" value="eyesear_white">
183-
<label class="form-check-label" for="theme_eyesearwhite">Eyesear White</label>
184-
<span class="button_example">Example</span>
185-
</div>
186-
<div class="form-check theme_preview_swarmpunk" id="theme_input_swarmpunk">
187-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_swarmpunk" value="swarmpunk">
188-
<label class="form-check-label" for="theme_swarmpunk">Swarm Punk</label>
189-
<span class="button_example">Example</span>
190-
</div>
191-
<div class="form-check theme_preview_beweish" id="theme_input_beweish">
192-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_beweish" value="beweish">
193-
<label class="form-check-label" for="theme_beweish">Beweish</label>
194-
<span class="button_example">Example</span>
195-
</div>
196-
<div class="form-check theme_preview_ctp_mocha" id="theme_input_ctp_mocha">
197-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_ctpmocha" value="ctp_mocha">
198-
<label class="form-check-label" for="theme_ctpmocha">Catppuccin Mocha</label>
199-
<span class="button_example">Example</span>
200-
</div>
201-
<div class="form-check theme_preview_ctp_macchiato" id="theme_input_ctp_macchiato">
202-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_ctpmacchiato" value="ctp_macchiato">
203-
<label class="form-check-label" for="theme_ctpmacchiato">Catppuccin Macchiato</label>
204-
<span class="button_example">Example</span>
205-
</div>
206-
<div class="form-check theme_preview_ctp_frappe" id="theme_input_ctp_frappe">
207-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_ctpfrappe" value="ctp_frappe">
208-
<label class="form-check-label" for="theme_ctpfrappe">Catppuccin Frappé</label>
209-
<span class="button_example">Example</span>
210-
</div>
211-
<div class="form-check theme_preview_ctp_latte" id="theme_input_ctp_latte">
212-
<input class="form-check-input" type="radio" name="theme_radio" id="theme_ctplatte" value="ctp_latte">
213-
<label class="form-check-label" for="theme_ctplatte">Catppuccin Latte</label>
214-
<span class="button_example">Example</span>
215-
</div>
146+
@foreach (var theme in Program.Web.RegisteredThemes.Values)
147+
{
148+
string themeName = $"{theme.Name.Replace(" (Legacy)","")}";
149+
string themeId = $"{theme.ID}";
150+
string themeIdNoUnderscore = $"{theme.ID.Replace("_", "")}";
151+
<div class="form-check theme_preview_@themeId" id="theme_input_@themeId">
152+
<input class="form-check-input" type="radio" name="theme_radio" id="theme_@themeIdNoUnderscore" value="@themeId" @(themeId == "modern_dark" ? "checked" : "")>
153+
<label class="form-check-label" for="theme_@themeIdNoUnderscore">@themeName</label>
154+
<iframe class="form-check-iframe" src="ThemePreview?theme=@themeId"></iframe>
155+
</div>
156+
}
216157
</fieldset>
217158
</div>
218159

src/Pages/Shared/_Layout.cshtml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
<title id="page_title">@ViewData["Title"] - SwarmUI - @Program.ServerSettings.UserAuthorization.InstanceTitle</title>
77
@{
88
string themeId = "modern_dark";
9+
var themeOverride = ViewData["theme_override"]?.ToString();
10+
if (!string.IsNullOrEmpty(themeOverride) && Program.Web.RegisteredThemes.ContainsKey(themeOverride))
11+
{
12+
themeId = themeOverride;
13+
}
914
// TODO: Identify user by auth header and use their settings to determine theme
10-
if (ViewContext.HttpContext.Request.Cookies.TryGetValue("sui_theme_id", out string themeCookie) && Program.Web.RegisteredThemes.ContainsKey(themeCookie)) {
15+
else if (ViewContext.HttpContext.Request.Cookies.TryGetValue("sui_theme_id", out string themeCookie) && Program.Web.RegisteredThemes.ContainsKey(themeCookie)) {
1116
themeId = themeCookie;
1217
}
1318
WebServer.ThemeData theme = Program.Web.RegisteredThemes[themeId];

src/Pages/ThemePreview.cshtml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
@page
2+
@{
3+
var themeOverride = Request.Query["theme"].ToString();
4+
ViewData["theme_override"] = themeOverride;
5+
}
6+
@section Header {
7+
<link rel="stylesheet" href="css/genpage.css?vary=@Utilities.VaryID" />
8+
<link rel="stylesheet" href="css/themepreview.css?vary=@Utilities.VaryID" />
9+
}
10+
<script>
11+
function reuseLastParamVal() {
12+
return null; // Prevent error in browser console
13+
}
14+
15+
function generateInputs() {
16+
const container = getRequiredElementById('input_group_content_coreparameters');
17+
container.innerHTML =
18+
makeNumberInput(null, 'input_images', 'images', 'Images', '', 1, 1, 10000, 1, 'big', false, true)
19+
+ makeNumberInput(null, 'input_seed', 'seed', 'Seed', '', -1, -1, 9223372036854776000, 1, 'seed', false, true)
20+
+ makeSliderInput(null, 'input_steps', 'steps', 'Steps', '', 20, 0, 500, 0, 100, 1, false, false, true)
21+
+ makeSliderInput(null, 'input_cfgscale', 'cfgscale', 'CFG Scale', '', 7, 0, 100, 0, 20, 0.5, false, false, true);
22+
autoNumberWidth(document.getElementById('input_images'));
23+
enableSliderAbove(document.getElementById('input_steps'));
24+
enableSliderAbove(document.getElementById('input_cfgscale'));
25+
}
26+
27+
document.addEventListener('DOMContentLoaded', function() {
28+
generateInputs();
29+
});
30+
</script>
31+
<div class="theme_preview-content">
32+
<div class="theme_preview-item noise-image-bg" id="theme_preview_coreparameters">
33+
<div class="input-group input-group-open" id="auto-group-coreparameters">
34+
<div class="input-group-content" id="input_group_content_coreparameters"></div>
35+
</div>
36+
</div>
37+
38+
<hr>
39+
40+
<div class="theme_preview-item">
41+
<div class="alt-prompt-buttons-wrapper">
42+
<button class="alt-prompt-buttons alt-prompt-generate-button basic-button translate" id="alt_generate_button">Generate</button>
43+
<button class="alt-prompt-buttons alt-prompt-center-button basic-button" id="alt_center_button">&#x2B9F;</button>
44+
<button class="alt-prompt-buttons interrupt-button interrupt-button-none alt-interrupt" id="alt_interrupt_button">&times;</button>
45+
</div>
46+
</div>
47+
</div>
48+
49+
@section Scripts {
50+
<script src="js/genpage/helpers/ui_improvements.js?vary=@Utilities.VaryID"></script>
51+
}

src/wwwroot/css/installer.css

Lines changed: 30 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -24,161 +24,42 @@
2424
}
2525
.form-check {
2626
margin-bottom: 0.5rem;
27+
cursor: pointer;
28+
}
29+
.theme_preview {
30+
max-height: 44rem;
31+
overflow-y: auto;
2732
}
2833
.theme_preview .form-check {
2934
display: inline-block;
30-
width: 30%;
31-
height: 10rem;
35+
width: 49%;
36+
height: 22rem;
3237
border: 1px solid var(--light-border);
3338
border-radius: 0.5rem;
39+
padding-left: 0;
40+
position: relative;
3441
}
35-
.theme_preview .form-check .button_example {
36-
display: block;
37-
height: 2rem;
38-
font-weight: bold;
39-
border-radius: 0.5rem;
40-
text-align: center;
41-
width: fit-content;
42-
padding-left: 0.2rem;
43-
padding-right: 0.2rem;
44-
margin-top: 2rem;
45-
margin-left: auto;
46-
margin-right: auto;
47-
padding-top: 3px;
48-
}
49-
.theme_preview_modern_dark {
50-
background-color: #202020;
51-
color: #E4E4E4;
52-
}
53-
.theme_preview_modern_dark .button_example {
54-
border: 1px solid #3f3f46;
55-
background-color: #4b4b4b;
56-
color: #ffffff;
57-
}
58-
.theme_preview_modern_light {
59-
background-color: #D9D9D9;
60-
color: #101010;
61-
}
62-
.theme_preview_modern_light .button_example {
63-
border: 1px solid #3f3f46;
64-
background-color: #a1a1a1;
65-
color: #101010;
66-
}
67-
.theme_preview_dark_dreams {
68-
background-color: #18181b;
69-
color: #eeeeee;
70-
}
71-
.theme_preview_dark_dreams .button_example {
72-
border: 1px solid #3f3f46;
73-
background-color: #7855e1;
74-
color: #ffffff;
75-
}
76-
.theme_preview_cyber_swarm {
77-
background-color: #18181b;
78-
color: #eeeeee;
79-
}
80-
.theme_preview_cyber_swarm .button_example {
81-
border: 1px solid #3f3f46;
82-
background-color: #00bc8c;
83-
color: #ffffff;
84-
border-radius: 1rem;
85-
padding-top: 0.1em;
86-
padding-left: 0.3em;
87-
padding-right: 0.3em;
88-
}
89-
.theme_preview_gravity_blue {
90-
background-color: #0b0f20;
91-
color: #eeeeee;
92-
}
93-
.theme_preview_gravity_blue .button_example {
94-
border: 1px solid #3f3f46;
95-
background-color: orange;
96-
color: #101010;
97-
}
98-
.theme_preview_eyesear_white {
99-
background-color: #f0f0f0;
100-
color: #101010;
101-
}
102-
.theme_preview_eyesear_white .button_example {
103-
border: 1px solid #3f3f46;
104-
background-color: #a1a1a1;
105-
color: #101010;
106-
}
107-
.theme_preview_punked {
108-
background-color: #0a0a0a;
109-
color: #eeeeee;
110-
}
111-
.theme_preview_punked .button_example {
112-
border: 1px solid #00fff7;
113-
background-color: #0a0a0a;
114-
color: #ffffff;
115-
border-radius: 1rem;
116-
padding-top: 0.1em;
117-
padding-left: 0.3em;
118-
padding-right: 0.3em;
119-
}
120-
.theme_preview_solarized {
121-
background-color: #d3cdc0;
122-
color: #101010;
123-
}
124-
.theme_preview_solarized .button_example {
125-
border: 1px solid #3f3f46;
126-
background-color: #a1a1a1;
127-
color: #101010;
128-
}
129-
.theme_preview_swarmpunk {
130-
background-color: #0a0a1f;
131-
color: #00ff9d;
132-
}
133-
.theme_preview_swarmpunk .button_example {
134-
border: 1px solid #00ff9d40;
135-
background-color: #12122a;
136-
color: #00ff9d;
137-
}
138-
.theme_preview_beweish {
139-
background-color: #0e0e2c;
140-
color: #ffffff;
141-
}
142-
.theme_preview_beweish .button_example {
143-
border: 1px solid #2a4170;
144-
background-color: #0f3460;
145-
color: #ffffff;
146-
}
147-
.theme_preview_ctp_mocha {
148-
background-color: #11111b;
149-
color: #cdd6f4;
150-
}
151-
.theme_preview_ctp_mocha .button_example {
152-
border: 1px solid #313244;
153-
background-color: #585b70;
154-
color: #cdd6f4;
155-
}
156-
.theme_preview_ctp_macchiato {
157-
background-color: #181926;
158-
color: #cad3f5;
159-
}
160-
.theme_preview_ctp_macchiato .button_example {
161-
border: 1px solid #363a4f;
162-
background-color: #5b6078;
163-
color: #cad3f5;
164-
}
165-
.theme_preview_ctp_frappe {
166-
background-color: #232634;
167-
color: #c6d0f5;
168-
}
169-
.theme_preview_ctp_frappe .button_example {
170-
border: 1px solid #414559;
171-
background-color: #626880;
172-
color: #c6d0f5;
173-
}
174-
.theme_preview_ctp_latte {
175-
background-color: #dce0e8;
176-
color: #4c4f69;
177-
}
178-
.theme_preview_ctp_latte .button_example {
179-
border: 1px solid #ccd0da;
180-
background-color: #acb0be;
181-
color: #4c4f69;
42+
.theme_preview .form-check:has(.form-check-input:checked) {
43+
border: 1px solid var(--emphasis);
44+
}
45+
.theme_preview label {
46+
margin-top: 0.125rem;
47+
margin-left: 0.5rem;
48+
cursor: pointer;
49+
}
50+
.form-check .form-check-input {
51+
margin-left: 0.5rem;
52+
margin-top: 0.5rem;
53+
}
54+
.form-check .form-check-iframe {
55+
width: 100%;
56+
height: calc(100% - 2rem);
57+
border-bottom-left-radius: calc(0.5rem - 1px);
58+
border-bottom-right-radius: calc(0.5rem - 1px);
59+
position: absolute;
60+
bottom: 0;
61+
left: 0;
62+
pointer-events: none;
18263
}
18364
label {
18465
font-size: 120%;

src/wwwroot/css/themepreview.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
html, body {
2+
height: 100%;
3+
}
4+
hr {
5+
margin: 0 0 1rem 0;
6+
}
7+
#alt_generate_button {
8+
margin-left: 0 !important;
9+
}
10+
.version-display {
11+
display: none;
12+
visibility: hidden;
13+
}
14+
.theme_preview-content {
15+
height: 100%;
16+
align-content: center;
17+
margin-top: -0.55rem;
18+
}
19+
.theme_preview-item {
20+
display: flex;
21+
justify-content: center;
22+
}
23+
.theme_preview-item .theme_preview-text {
24+
font-size: 1.25rem;
25+
margin-bottom: 0;
26+
}
27+
.theme_preview-item.noise-image-bg {
28+
background-image: var(--noise-image);
29+
padding: 1rem 0;
30+
}
31+
.input-group.input-group-open {
32+
display: block;
33+
margin: 0 0.5rem;
34+
}
35+
.input-group.input-group-open .input-group-content {
36+
margin-top: 0;
37+
}

0 commit comments

Comments
 (0)