The rest of the Control Panel (exc. the Group Editor) now uses dyntmpl.

Improved the noavatar cacheability by constraining them in a range.
Improved the noavatar cacheability by serving a small subset from Gosora.
Improved the formatting of bytes in the memory analytics pane.
Static resources with checksums will now have their caches expire in a week rather than a day.

Tweaked the padding on the sub_heads on Nox.
Moved a block of CSS out of a template and into panel.css in Tempra Simple and Shadow.

Added the panel_themes_menus_items_suffix phrase.

Added the DisableNoavatarRange config setting.
Added the DisableDefaultNoavatar config setting.
This commit is contained in:
Azareal 2019-05-03 18:34:18 +10:00
parent a8e1076f7c
commit a1161e20f3
38 changed files with 129 additions and 166 deletions

View File

@ -1,7 +1,7 @@
/*
*
* Gosora Alerts System
* Copyright Azareal 2017 - 2019
* Copyright Azareal 2017 - 2020
*
*/
package common

View File

@ -96,6 +96,8 @@ type config struct {
LoosePort bool
DisableServerPush bool
EnableCDNPush bool
DisableNoavatarRange bool
DisableDefaultNoavatar bool
Noavatar string // ? - Move this into the settings table?
ItemsPerPage int // ? - Move this into the settings table?

View File

@ -1,7 +1,7 @@
/*
*
* Gosora User File
* Copyright Azareal 2017 - 2019
* Copyright Azareal 2017 - 2020
*
*/
package common
@ -456,13 +456,36 @@ type GuestAvatar struct {
}
func buildNoavatar(uid int, width int) string {
if !Config.DisableNoavatarRange {
// TODO: Find a faster algorithm
if uid > 50000 {
uid -= 50000
}
if uid > 5000 {
uid -= 5000
}
if uid > 500 {
uid -= 500
}
for uid > 50 {
uid -= 50
}
}
if !Config.DisableDefaultNoavatar && uid < 5 {
return "/static/n"+strconv.Itoa(uid)+"-"+strconv.Itoa(width)+".png?i=0"
}
return strings.Replace(strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(uid), 1), "{width}", strconv.Itoa(width), 1)
}
// ? Make this part of *User?
// TODO: Write tests for this
func BuildAvatar(uid int, avatar string) (normalAvatar string, microAvatar string) {
if avatar != "" {
if avatar == "" {
if uid == 0 {
return guestAvatar.Normal, guestAvatar.Micro
}
return buildNoavatar(uid, 200), buildNoavatar(uid, 48)
}
if avatar[0] == '.' {
if avatar[1] == '.' {
normalAvatar = "/uploads/avatar_" + strconv.Itoa(uid) + "_tmp" + avatar[1:]
@ -473,11 +496,6 @@ func BuildAvatar(uid int, avatar string) (normalAvatar string, microAvatar strin
}
return avatar, avatar
}
if uid == 0 {
return guestAvatar.Normal, guestAvatar.Micro
}
return buildNoavatar(uid, 200), buildNoavatar(uid, 48)
}
// TODO: Move this to *User
func SetPassword(uid int, password string) error {

View File

@ -92,6 +92,10 @@ DisableServerPush - This switch lets you disable the HTTP/2 server push feature.
EnableCDNPush - This switch lets you enable the HTTP/2 CDN Server Push feature. This operates by sending a Link header on every request and may also work with reverse-proxies like Nginx for doing HTTP/2 server pushes.
DisableNoavatarRange - This switch lets you disable the noavatar algorithm which maps IDs to a set ranging from 0 to 50 for better cacheability. Default: false
DisableDefaultNoavatar - This switch lets you disable the default noavatar algorithm which may intercept noavatars for increased efficiency. Default: false
NoAvatar - The default avatar to use for users when they don't have their own. The default for this may change in the near future to better utilise HTTP/2. Example: https://api.adorable.io/avatars/{width}/{id}.png
ItemsPerPage - The number of posts, topics, etc. you want on each page.

View File

@ -960,6 +960,7 @@
"panel_themes_menus_head":"Menus",
"panel_themes_menus_main":"Main Menu",
"panel_themes_menus_items_suffix":" items",
"panel_themes_menus_items_head":"Menu Items",
"panel_themes_menus_items_edit_button_aria":"Edit menu item",
"panel_themes_menus_items_delete_button_aria":"Delete menu item",

View File

@ -8,13 +8,20 @@ const Gigabyte = Megabyte * 1024;
const Terabyte = Gigabyte * 1024;
const Petabyte = Terabyte * 1024;
function convertByteUnit(bytes) {
if(bytes >= Petabyte) return Math.ceil(bytes / Petabyte) + "PB";
else if(bytes >= Terabyte) return Math.ceil(bytes / Terabyte) + "TB";
else if(bytes >= Gigabyte) return Math.ceil(bytes / Gigabyte) + "GB";
else if(bytes >= Megabyte) return Math.ceil(bytes / Megabyte) + "MB";
else if(bytes >= Kilobyte) return Math.ceil(bytes / Kilobyte) + "KB";
return bytes;
function convertByteUnit(bytes, places = 0) {
let out;
if(bytes >= Petabyte) out = [bytes / Petabyte, "PB"];
else if(bytes >= Terabyte) out = [bytes / Terabyte, "TB"];
else if(bytes >= Gigabyte) out = [bytes / Gigabyte, "GB"];
else if(bytes >= Megabyte) out = [bytes / Megabyte, "MB"];
else if(bytes >= Kilobyte) out = [bytes / Kilobyte, "KB"];
else out = [bytes,"b"];
if(places==0) return Math.ceil(out[0]) + out[1];
else {
let ex = Math.pow(10, places);
return (Math.round(out[0], ex) / ex) + out[1];
}
}
// TODO: Fully localise this

BIN
public/n1-200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/n1-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
public/n2-200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/n2-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
public/n3-200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
public/n3-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/n4-200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/n4-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -14,6 +14,7 @@ import (
)
var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(c.Day)) // TODO: Make this a c.Config value
var cacheControlMaxAgeWeek = "max-age=" + strconv.Itoa(int(c.Week)) // TODO: Make this a c.Config value
// GET functions
func StaticFile(w http.ResponseWriter, r *http.Request) {
@ -33,7 +34,11 @@ func StaticFile(w http.ResponseWriter, r *http.Request) {
}
h.Set("Last-Modified", file.FormattedModTime)
h.Set("Content-Type", file.Mimetype)
if len(file.Sha256) != 0 {
h.Set("Cache-Control", cacheControlMaxAgeWeek)
} else {
h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000
}
h.Set("Vary", "Accept-Encoding")
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && file.GzipLength > 0 {

View File

@ -57,6 +57,5 @@ func Backups(w http.ResponseWriter, r *http.Request, user c.User, backupURL stri
backupList = append(backupList, c.BackupItem{backupFile.Name(), backupFile.ModTime()})
}
pi := c.PanelBackupPage{basePage, backupList}
return renderTemplate("panel_backups", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_backups", c.PanelBackupPage{basePage, backupList}})
}

View File

@ -42,5 +42,5 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
runtime.ReadMemStats(&memStats)
pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats}
return renderTemplate("panel_debug", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_dashboard_right","","panel_debug", pi})
}

View File

@ -34,7 +34,7 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
pageList := c.Paginate(logCount, perPage, 5)
pi := c.PanelRegLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel_reglogs", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_reglogs", pi})
}
// TODO: Log errors when something really screwy is going on?
@ -125,7 +125,7 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
pageList := c.Paginate(logCount, perPage, 5)
pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel_modlogs", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_modlogs", pi})
}
func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
@ -152,5 +152,5 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
pageList := c.Paginate(logCount, perPage, 5)
pi := c.PanelLogsPage{basePage, llist, c.Paginator{pageList, page, lastPage}}
return renderTemplate("panel_adminlogs", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_adminlogs", pi})
}

View File

@ -22,8 +22,7 @@ func Plugins(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
pluginList = append(pluginList, plugin)
}
pi := c.PanelPage{basePage, pluginList, nil}
return renderTemplate("panel_plugins", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_plugins", c.PanelPage{basePage, pluginList, nil}})
}
// TODO: Abstract more of the plugin activation / installation / deactivation logic, so we can test all that more reliably and easily

View File

@ -34,7 +34,7 @@ func Themes(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
}
pi := c.PanelThemesPage{basePage, pThemeList, vThemeList}
return renderTemplate("panel_themes", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_themes","","panel_themes",&pi})
}
func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user c.User, uname string) c.RouteError {
@ -85,8 +85,7 @@ func ThemesMenus(w http.ResponseWriter, r *http.Request, user c.User) c.RouteErr
})
}
pi := c.PanelMenuListPage{basePage, menuList}
return renderTemplate("panel_themes_menus", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_themes_menus", &c.PanelMenuListPage{basePage, menuList}})
}
func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user c.User, smid string) c.RouteError {
@ -133,8 +132,7 @@ func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user c.User, smid s
menuList = append(menuList, item)
}
pi := c.PanelMenuPage{basePage, mid, menuList}
return renderTemplate("panel_themes_menus_items", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_themes_menus_items", &c.PanelMenuPage{basePage, mid, menuList}})
}
func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user c.User, sitemID string) c.RouteError {
@ -159,8 +157,7 @@ func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user c.User, sit
return c.InternalError(err, w, r)
}
pi := c.PanelMenuItemPage{basePage, menuItem}
return renderTemplate("panel_themes_menus_item_edit", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_themes_menus_item_edit", &c.PanelMenuItemPage{basePage, menuItem}})
}
func themesMenuItemSetters(r *http.Request, menuItem c.MenuItem) c.MenuItem {
@ -362,7 +359,7 @@ func ThemesWidgets(w http.ResponseWriter, r *http.Request, user c.User) c.RouteE
}
pi := c.PanelWidgetListPage{basePage, docks, c.WidgetEdit{&c.Widget{ID: 0, Type: "simple"}, make(map[string]string)}}
return renderTemplate("panel_themes_widgets", w, r, basePage.Header, &pi)
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_themes_widgets", pi})
}
func widgetsParseInputs(r *http.Request, widget *c.Widget) (*c.WidgetEdit, error) {

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
</div>
@ -21,6 +16,3 @@
{{end}}
</div>
{{template "paginator.html" . }}
</main>
</div>
{{template "footer.html" . }}

View File

@ -48,15 +48,33 @@
options = Chartist.extend({}, {}, options);
return function byteUnits(chart) {
if (chart instanceof Chartist.Line) {
if(!chart instanceof Chartist.Line) return;
chart.on('created', function() {
console.log("running created")
let vbits = document.getElementsByClassName("ct-vertical");
const vbits = document.getElementsByClassName("ct-vertical");
if(vbits==null) return;
let tbits = [];
for(let i = 0; i < vbits.length; i++) {
vbits[i].innerHTML = convertByteUnit(vbits[i].innerHTML);
tbits[i] = vbits[i].innerHTML;
}
const calc = (places) => {
if(places==3) return;
const matcher = vbits[0].innerHTML;
let allMatch = true;
for(let i = 0; i < tbits.length; i++) {
let val = convertByteUnit(tbits[i], places);
if(val!=matcher) allMatch = false;
vbits[i].innerHTML = val;
}
if(allMatch) calc(places + 1);
}
calc(0);
});
}
};
};

View File

@ -16,7 +16,7 @@
<div id="panel_analytics_posts_table" class="colstack_item rowlist" aria-label="{{lang "panel_statistics_post_counts_table_aria"}}">
{{range .ViewItems}}
<div class="rowitem panel_compactrow editable_parent">
<a class="panel_upshift {{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}unix_to_24_hour_time{{else}}unix_to_date{{end}}">{{.Time}}</a>
<a class="panel_upshift unix_{{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}to_24_hour_time{{else}}to_date{{end}}">{{.Time}}</a>
<span class="panel_compacttext to_right">{{.Count}}{{lang "panel_statistics_posts_suffix"}}</span>
</div>
{{else}}<div class="rowitem passive rowmsg">{{lang "panel_statistics_post_counts_no_post_counts"}}</div>{{end}}

View File

@ -16,7 +16,7 @@
<div id="panel_analytics_topics_table" class="colstack_item rowlist" aria-label="{{lang "panel_statistics_topic_counts_table_aria"}}">
{{range .ViewItems}}
<div class="rowitem panel_compactrow editable_parent">
<a class="panel_upshift {{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}unix_to_24_hour_time{{else}}unix_to_date{{end}}">{{.Time}}</a>
<a class="panel_upshift unix_{{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}to_24_hour_time{{else}}to_date{{end}}">{{.Time}}</a>
<span class="panel_compacttext to_right">{{.Count}}{{lang "panel_statistics_topics_suffix"}}</span>
</div>
{{end}}

View File

@ -16,7 +16,7 @@
<div id="panel_analytics_views_table" class="colstack_item rowlist" aria-label="{{lang "panel_statistics_requests_table_aria"}}">
{{range .ViewItems}}
<div class="rowitem panel_compactrow editable_parent">
<a class="panel_upshift {{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}unix_to_24_hour_time{{else}}unix_to_date{{end}}">{{.Time}}</a>
<a class="panel_upshift unix_{{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}to_24_hour_time{{else}}to_date{{end}}">{{.Time}}</a>
<span class="panel_compacttext to_right">{{.Count}}{{lang "panel_statistics_views_suffix"}}</span>
</div>
{{end}}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_backups_head"}}</h1></div>
</div>
@ -18,6 +13,3 @@
<div class="rowitem rowmsg">{{lang "panel_backups_no_backups"}}</div>
{{end}}
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main id="panel_dashboard_right" class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_debug_head"}}</h1></div>
</div>
@ -49,6 +44,3 @@
<div class="grid_item grid_stat"><span>?</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.StackInuse}}</span></div>
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
</div>
@ -21,6 +16,3 @@
{{end}}
</div>
{{template "paginator.html" . }}
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,9 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_plugins_head"}}</h1></div>
</div>
@ -25,7 +19,3 @@
</div>
{{end}}
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_logs_registration_head"}}</h1></div>
</div>
@ -21,6 +16,3 @@
{{end}}
</div>
{{template "paginator.html" . }}
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,17 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
{{/** Stop inlining this x.x **/}}
<style type="text/css">
.rowitem::after {
content: "";
display: block;
clear: both;
}
</style>
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_themes_primary_themes"}}</h1></div>
</div>
@ -50,7 +36,3 @@
{{end}}
</div>
{{end}}
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
</div>
@ -10,10 +5,7 @@
{{range .ItemList}}
<div class="rowitem panel_compactrow editable_parent">
<a href="/panel/themes/menus/edit/{{.ID}}" class="editable_block panel_upshift">{{if .Name}}{{.Name}} - {{end}}#{{.ID}}</a>
<a class="panel_compacttext to_right">{{.ItemCount}} items</a>
<a class="panel_compacttext to_right">{{.ItemCount}}{{lang "panel_themes_menus_items_suffix"}}</a>
</div>
{{end}}
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,10 +1,5 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{/** TODO: Set the order based on the order here **/}}
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
</div>
@ -59,11 +54,9 @@
</select></div>
</div>
<div class="formrow">
<div class="formitem"><button name="panel-button" class="formbutton">{{lang "panel_themes_menus_edit_update_button"}}</button></div>
<div class="formitem">
<button name="panel-button" class="formbutton">{{lang "panel_themes_menus_edit_update_button"}}</button>
</div>
</div>
</div>
</form>
</main>
</div>
{{template "footer.html" . }}

View File

@ -1,8 +1,3 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem">
<h1>{{lang "panel_themes_menus_items_head"}}</h1>
@ -78,6 +73,3 @@
</div>
</div>
</form>
</main>
</div>
{{template "footer.html" . }}

View File

@ -9,11 +9,6 @@ type Widget struct {
Literal bool
}
**/}}
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel_menu.html" . }}
<main class="colstack_right">
{{template "panel_before_head.html" . }}
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_themes_widgets_head"}}</h1></div>
</div>
@ -56,6 +51,3 @@ type Widget struct {
</div>
</div>
</div>
</main>
</div>
{{template "footer.html" . }}

View File

@ -87,7 +87,7 @@
background-color: #444444;
}
.colstack_right .colstack_head.colstack_sub_head:not(:first-child) {
margin-top: 6px;
margin-top: 12px;
}
.colstack_head + .colstack_head.colstack_sub_head:not(:first-child) {
margin-top: 2px;

View File

@ -81,6 +81,12 @@
padding-right: 2px;
}
#panel_themes .rowitem::after {
content: "";
display: block;
clear: both;
}
.panel_submitrow .rowitem {
display: flex;
}

View File

@ -188,3 +188,9 @@
.wtype_about .w_about, .wtype_simple .w_simple, .wtype_wol .w_wol, .wtype_default .w_default {
display: block;
}
#panel_themes .rowitem::after {
content: "";
display: block;
clear: both;
}