Expanded upon the control panel debug page and improved the UI a bit.

Trying to de-dupe the phrase strings to avoid allocating as much memory and creating as many pointers.
Shortened topic.status_closed_aria slightly.
Moved some things that should have been in various panel.css' into those panel.css'
Fixed the MemUsed dashboard item on Cosora.

Added the bunit template function.
This commit is contained in:
Azareal 2019-05-13 19:17:44 +10:00
parent 30722e1013
commit 9cd3cbadab
13 changed files with 213 additions and 108 deletions

View File

@ -282,25 +282,25 @@ type ResetPage struct {
/* WIP for dyntmpl */ /* WIP for dyntmpl */
type Panel struct { type Panel struct {
*BasePanelPage *BasePanelPage
HTMLID string HTMLID string
ClassNames string ClassNames string
TmplName string TmplName string
Inner nobreak Inner nobreak
} }
type PanelAnalytics struct { type PanelAnalytics struct {
*BasePanelPage *BasePanelPage
FormAction string FormAction string
TmplName string TmplName string
Inner nobreak Inner nobreak
} }
type PanelAnalyticsStd struct{ type PanelAnalyticsStd struct {
Graph PanelTimeGraph Graph PanelTimeGraph
ViewItems []PanelAnalyticsItem ViewItems []PanelAnalyticsItem
TimeRange string TimeRange string
Unit string Unit string
TimeType string TimeType string
} }
type PanelAnalyticsStdUnit struct{ type PanelAnalyticsStdUnit struct {
Graph PanelTimeGraph Graph PanelTimeGraph
ViewItems []PanelAnalyticsItemUnit ViewItems []PanelAnalyticsItemUnit
TimeRange string TimeRange string
@ -334,7 +334,7 @@ type PanelPage struct {
type GridElement struct { type GridElement struct {
ID string ID string
Href string Href string
Body string Body string
Order int // For future use Order int // For future use
Class string Class string
@ -387,7 +387,7 @@ type PanelAnalyticsItem struct {
type PanelAnalyticsItemUnit struct { type PanelAnalyticsItemUnit struct {
Time int64 Time int64
Count int64 Count int64
Unit string Unit string
} }
type PanelAnalyticsPage struct { type PanelAnalyticsPage struct {
@ -591,6 +591,10 @@ type PanelDebugPage struct {
Goroutines int Goroutines int
CPUs int CPUs int
MemStats runtime.MemStats MemStats runtime.MemStats
TCache int
UCache int
TopicListThaw bool
} }
type PageSimple struct { type PageSimple struct {

View File

@ -99,6 +99,23 @@ func InitPhrases(lang string) error {
// [prefix][name]phrase // [prefix][name]phrase
langPack.TmplPhrasesPrefixes = make(map[string]map[string]string) langPack.TmplPhrasesPrefixes = make(map[string]map[string]string)
var conMap = make(map[string]string) // Cache phrase strings so we can de-dupe items to reduce memory use. There appear to be some minor improvements with this, although we would need a more thorough check to be sure.
for name, phrase := range langPack.TmplPhrases {
_, ok := conMap[phrase]
if !ok {
conMap[phrase] = phrase
}
cItem := conMap[phrase]
prefix := strings.Split(name, ".")[0]
_, ok = langPack.TmplPhrasesPrefixes[prefix]
if !ok {
langPack.TmplPhrasesPrefixes[prefix] = make(map[string]string)
}
langPack.TmplPhrasesPrefixes[prefix][name] = cItem
}
// [prefix][name]phrase
/*langPack.TmplPhrasesPrefixes = make(map[string]map[string]string)
for name, phrase := range langPack.TmplPhrases { for name, phrase := range langPack.TmplPhrases {
prefix := strings.Split(name, ".")[0] prefix := strings.Split(name, ".")[0]
_, ok := langPack.TmplPhrasesPrefixes[prefix] _, ok := langPack.TmplPhrasesPrefixes[prefix]
@ -106,7 +123,7 @@ func InitPhrases(lang string) error {
langPack.TmplPhrasesPrefixes[prefix] = make(map[string]string) langPack.TmplPhrasesPrefixes[prefix] = make(map[string]string)
} }
langPack.TmplPhrasesPrefixes[prefix][name] = phrase langPack.TmplPhrasesPrefixes[prefix][name] = phrase
} }*/
langPack.TmplIndicesToPhrases = make([][][]byte, len(langTmplIndicesToNames)) langPack.TmplIndicesToPhrases = make([][][]byte, len(langTmplIndicesToNames))
for tmplID, phraseNames := range langTmplIndicesToNames { for tmplID, phraseNames := range langTmplIndicesToNames {

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"fmt"
"html/template" "html/template"
"io" "io"
"log" "log"
@ -301,7 +302,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
tmpls.AddStd("account", "common.Account", accountPage) tmpls.AddStd("account", "common.Account", accountPage)
basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID} basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID}
tmpls.AddStd("panel", "common.Panel", Panel{basePage, "panel_dashboard_right","","panel_dashboard", inter}) tmpls.AddStd("panel", "common.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter})
//tmpls.AddStd("panel_analytics", "common.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter}) //tmpls.AddStd("panel_analytics", "common.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
var writeTemplate = func(name string, content interface{}) { var writeTemplate = func(name string, content interface{}) {
@ -688,6 +689,22 @@ func initDefaultTmplFuncMap() {
return template.HTML(phrases.GetLevelPhrase(level)) return template.HTML(phrases.GetLevelPhrase(level))
} }
fmap["bunit"] = func(byteInt interface{}) interface{} {
var byteFloat float64
var unit string
switch bytes := byteInt.(type) {
case int:
byteFloat, unit = ConvertByteUnit(float64(bytes))
case int64:
byteFloat, unit = ConvertByteUnit(float64(bytes))
case uint64:
byteFloat, unit = ConvertByteUnit(float64(bytes))
default:
panic("bytes is not an int, int64 or uint64")
}
return fmt.Sprintf("%.1f", byteFloat) + unit
}
fmap["abstime"] = func(timeInt interface{}) interface{} { fmap["abstime"] = func(timeInt interface{}) interface{} {
time, ok := timeInt.(time.Time) time, ok := timeInt.(time.Time)
if !ok { if !ok {

View File

@ -74,7 +74,7 @@ type CTemplateSet struct {
logger *log.Logger logger *log.Logger
loggerf *os.File loggerf *os.File
lang string lang string
} }
func NewCTemplateSet(in string) *CTemplateSet { func NewCTemplateSet(in string) *CTemplateSet {
@ -108,6 +108,7 @@ func NewCTemplateSet(in string) *CTemplateSet {
"lang": true, "lang": true,
//"langf":true, //"langf":true,
"level": true, "level": true,
"bunit": true,
"abstime": true, "abstime": true,
"reltime": true, "reltime": true,
"scope": true, "scope": true,
@ -117,7 +118,7 @@ func NewCTemplateSet(in string) *CTemplateSet {
}, },
logger: log.New(f, "", log.LstdFlags), logger: log.New(f, "", log.LstdFlags),
loggerf: f, loggerf: f,
lang:in, lang: in,
} }
} }
@ -443,13 +444,13 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
} }
if c.lang == "normal" { if c.lang == "normal" {
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n" fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `) fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
if !ok { if !ok {
return errors.New("invalid page struct value") return errors.New("invalid page struct value")
} }
` `
fout += `var iw http.ResponseWriter fout += `var iw http.ResponseWriter
gzw, ok := w.(common.GzipResponseWriter) gzw, ok := w.(common.GzipResponseWriter)
if ok { if ok {
iw = gzw.ResponseWriter iw = gzw.ResponseWriter
@ -1116,6 +1117,17 @@ ArgLoop:
litString("phrases.GetLevelPhrase("+leftParam+")", false) litString("phrases.GetLevelPhrase("+leftParam+")", false)
c.importMap[langPkg] = langPkg c.importMap[langPkg] = langPkg
break ArgLoop break ArgLoop
case "bunit":
// TODO: Implement bunit literals
leftOperand := node.Args[pos+1].String()
if len(leftOperand) == 0 {
panic("The leftoperand for function buint cannot be left blank")
}
leftParam, _ := c.compileIfVarSub(con, leftOperand)
out = "{\nbyteFloat, unit := common.ConvertByteUnit(float64(" + leftParam + "))\n"
out += "w.Write(fmt.Sprintf(\"%.1f\", byteFloat) + unit)\n"
literal = true
break ArgLoop
case "abstime": case "abstime":
// TODO: Implement level literals // TODO: Implement level literals
leftOperand := node.Args[pos+1].String() leftOperand := node.Args[pos+1].String()
@ -1407,7 +1419,7 @@ func (c *CTemplateSet) retCall(name string, params ...interface{}) {
c.detail("returned from " + name + " => (" + pstr + ")") c.detail("returned from " + name + " => (" + pstr + ")")
} }
func buildUserExprs(holder string) ([]string,[]string) { func buildUserExprs(holder string) ([]string, []string) {
var userExprs = []string{ var userExprs = []string{
holder + ".CurrentUser.Loggedin", holder + ".CurrentUser.Loggedin",
holder + ".CurrentUser.IsSuperMod", holder + ".CurrentUser.IsSuperMod",

View File

@ -125,37 +125,37 @@ func (mts *MemoryTopicCache) Remove(id int) error {
} }
// RemoveUnsafe is the unsafe version of Remove. THIS METHOD IS NOT THREAD-SAFE. // RemoveUnsafe is the unsafe version of Remove. THIS METHOD IS NOT THREAD-SAFE.
func (mts *MemoryTopicCache) RemoveUnsafe(id int) error { func (s *MemoryTopicCache) RemoveUnsafe(id int) error {
_, ok := mts.items[id] _, ok := s.items[id]
if !ok { if !ok {
return ErrNoRows return ErrNoRows
} }
delete(mts.items, id) delete(s.items, id)
atomic.AddInt64(&mts.length, -1) atomic.AddInt64(&s.length, -1)
return nil return nil
} }
// Flush removes all the topics from the cache, useful for tests. // Flush removes all the topics from the cache, useful for tests.
func (mts *MemoryTopicCache) Flush() { func (s *MemoryTopicCache) Flush() {
mts.Lock() s.Lock()
mts.items = make(map[int]*Topic) s.items = make(map[int]*Topic)
mts.length = 0 s.length = 0
mts.Unlock() s.Unlock()
} }
// ! Is this concurrent? // ! Is this concurrent?
// Length returns the number of topics in the memory cache // Length returns the number of topics in the memory cache
func (mts *MemoryTopicCache) Length() int { func (s *MemoryTopicCache) Length() int {
return int(mts.length) return int(s.length)
} }
// SetCapacity sets the maximum number of topics which this cache can hold // SetCapacity sets the maximum number of topics which this cache can hold
func (mts *MemoryTopicCache) SetCapacity(capacity int) { func (s *MemoryTopicCache) SetCapacity(capacity int) {
// Ints are moved in a single instruction, so this should be thread-safe // Ints are moved in a single instruction, so this should be thread-safe
mts.capacity = capacity s.capacity = capacity
} }
// GetCapacity returns the maximum number of topics this cache can hold // GetCapacity returns the maximum number of topics this cache can hold
func (mts *MemoryTopicCache) GetCapacity() int { func (s *MemoryTopicCache) GetCapacity() int {
return mts.capacity return s.capacity
} }

View File

@ -589,7 +589,7 @@
"forums_no_forums":"You don't have access to any forums.", "forums_no_forums":"You don't have access to any forums.",
"topic.opening_post_aria":"The opening post for this topic", "topic.opening_post_aria":"The opening post for this topic",
"topic.status_closed_aria":"This topic has been locked", "topic.status_closed_aria":"This topic is locked",
"topic.title_input_aria":"Topic Title Input", "topic.title_input_aria":"Topic Title Input",
"topic.update_button":"Update", "topic.update_button":"Update",
"topic.userinfo_aria":"The information on the poster", "topic.userinfo_aria":"The information on the poster",

View File

@ -41,6 +41,17 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
var memStats runtime.MemStats var memStats runtime.MemStats
runtime.ReadMemStats(&memStats) runtime.ReadMemStats(&memStats)
pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats} var tlen, ulen int
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_dashboard_right","","panel_debug", pi}) tcache := c.Topics.GetCache()
if tcache != nil {
tlen = tcache.Length()
}
ucache := c.Users.GetCache()
if ucache != nil {
ulen = ucache.Length()
}
topicListThawed := c.TopicListThaw.Thawed()
pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats, tlen, ulen, topicListThawed}
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_dashboard_right", "debug_page", "panel_debug", pi})
} }

View File

@ -20,27 +20,73 @@
<div class="grid_item grid_stat"><span>{{.DBAdapter}}</span></div> <div class="grid_item grid_stat"><span>{{.DBAdapter}}</span></div>
<div class="grid_item grid_stat"><span>?</span></div> <div class="grid_item grid_stat"><span>?</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>{{lang "panel_debug_goroutine_count_label"}}</span></div> <div class="grid_item grid_stat grid_stat_head"><span>{{lang "panel_debug_goroutine_count_label"}}</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>{{lang "panel_debug_cpu_count_label"}}</span></div> <div class="grid_item grid_stat grid_stat_head"><span>{{lang "panel_debug_cpu_count_label"}}</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>HeapAlloc</span></div> <div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
<div class="grid_item grid_stat"><span>{{.Goroutines}}</span></div> <div class="grid_item grid_stat"><span>{{.Goroutines}}</span></div>
<div class="grid_item grid_stat"><span>{{.CPUs}}</span></div> <div class="grid_item grid_stat"><span>{{.CPUs}}</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapAlloc}}</span></div> <div class="grid_item grid_stat"><span>?</span></div>
</div>
<div class="colstack_item colstack_head colstack_sub_head">
<div class="rowitem"><h2>Memory Statistics</h2></div>
</div>
<div id="panel_debug" class="colstack_grid">
<div class="grid_item grid_stat grid_stat_head"><span>Sys</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>HeapSys</span></div> <div class="grid_item grid_stat grid_stat_head"><span>HeapSys</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>HeapAlloc</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.Sys}} ({{bunit .MemStats.Sys}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapSys}} ({{bunit .MemStats.HeapSys}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapAlloc}} ({{bunit .MemStats.HeapAlloc}})</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>HeapIdle</span></div> <div class="grid_item grid_stat grid_stat_head"><span>HeapIdle</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>HeapObjects</span></div> <div class="grid_item grid_stat grid_stat_head"><span>HeapObjects</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapSys}}</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapIdle}}</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.HeapObjects}}</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>StackInuse</span></div> <div class="grid_item grid_stat grid_stat_head"><span>StackInuse</span></div>
<div class="grid_item grid_stat"><span>?</span></div> <div class="grid_item grid_stat"><span>{{.MemStats.HeapIdle}} ({{bunit .MemStats.HeapIdle}})</span></div>
<div class="grid_item grid_stat"><span>?</span></div> <div class="grid_item grid_stat"><span>{{.MemStats.HeapObjects}}</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.StackInuse}}</span></div> <div class="grid_item grid_stat"><span>{{.MemStats.StackInuse}} ({{bunit .MemStats.StackInuse}})</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>MSpanInuse</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>MCacheInuse</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>MSpanSys</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.MSpanInuse}} ({{bunit .MemStats.MSpanInuse}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.MCacheInuse}} ({{bunit .MemStats.MCacheInuse}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.MSpanSys}} ({{bunit .MemStats.MSpanSys}})</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>MCacheSys</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>GCSys</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>OtherSys</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.MCacheSys}} ({{bunit .MemStats.MCacheSys}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.GCSys}} ({{bunit .MemStats.GCSys}})</span></div>
<div class="grid_item grid_stat"><span>{{.MemStats.OtherSys}} ({{bunit .MemStats.OtherSys}})</span></div>
</div>
<div class="colstack_item colstack_head colstack_sub_head">
<div class="rowitem"><h2>Caches</h2></div>
</div>
<div id="panel_debug" class="colstack_grid">
<div class="grid_item grid_stat grid_stat_head"><span>Topic Cache</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>User Cache</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>Reply Cache</span></div>
<div class="grid_item grid_stat"><span>{{.TCache}}</span></div>
<div class="grid_item grid_stat"><span>{{.UCache}}</span></div>
<div class="grid_item grid_stat"><span>0</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>Topic List</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
<div class="grid_item grid_stat"><span>{{if .TopicListThaw}}Thawed{{else}}Sleeping{{end}}</span></div>
<div class="grid_item grid_stat"><span>?</span></div>
<div class="grid_item grid_stat"><span>?</span></div>
</div> </div>

View File

@ -1594,42 +1594,13 @@ red {
padding-bottom: 0px; padding-bottom: 0px;
padding-right: 10px; padding-right: 10px;
} }
.grid_item span { .grid_item span, .grid_item a {
margin-top: 16px; margin-top: 16px;
margin-bottom: 16px; margin-bottom: 16px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
text-align: center; text-align: center;
} }
/* TODO: Move these to panel.css */
#dash-version:before, #dash-cpu:before, #dash-ram:before, #dash-totonline:before, #dash-gonline:before, #dash-uonline:before, #dash-reqs:before, #dash-postsperday:before, #dash-topicsperday:before {
display: inline-block;
background: var(--tinted-background-color);
font: normal normal normal 14px/1 FontAwesome;
font-size: 20px;
padding-left: 17px;
padding-top: 16px;
padding-right: 19px;
color: hsl(0,0%,20%);
}
#dash-version:before {
content: "\f126";
}
#dash-cpu:before {
content: "\f2db";
}
#dash-ram:before {
content: "\f233";
}
#dash-totonline:before, #dash-gonline:before, #dash-uonline:before {
content: "\f007";
}
#dash-reqs:before {
content: "\f080";
}
#dash-postsperday:before, #dash-topicsperday:before {
content: "\f27b";
}
@media(min-width: 1000px) { @media(min-width: 1000px) {
.footer { .footer {
@ -1648,13 +1619,6 @@ red {
max-width: 1000px; max-width: 1000px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
}
.footer {
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
#main {
padding-top: 18px; padding-top: 18px;
padding-left: 16px; padding-left: 16px;
padding-right: 16px; padding-right: 16px;
@ -1662,6 +1626,9 @@ red {
border-right: 1px solid hsl(20,0%,95%); border-right: 1px solid hsl(20,0%,95%);
} }
.footer { .footer {
max-width: 1000px;
margin-left: auto;
margin-right: auto;
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
} }
@ -1686,10 +1653,7 @@ red {
.like_count { .like_count {
margin-right: 1px; margin-right: 1px;
} }
.like_count:after { .like_count:after, .created_at:before, .ip_item:before {
margin-right: 6px;
}
.created_at:before, .ip_item:before {
margin-right: 6px; margin-right: 6px;
} }
} }

View File

@ -80,6 +80,41 @@
font-size: 17px; font-size: 17px;
color: hsl(0,0%,40%); color: hsl(0,0%,40%);
} }
/* TODO: Move these to panel.css */
#dash-version:before, #dash-cpu:before, #dash-ram:before, #dash-memused:before, #dash-totonline:before, #dash-gonline:before, #dash-uonline:before, #dash-reqs:before, #dash-postsperday:before, #dash-topicsperday:before {
display: inline-block;
background: var(--tinted-background-color);
font: normal normal normal 14px/1 FontAwesome;
font-size: 20px;
padding-left: 17px;
padding-top: 16px;
padding-right: 19px;
color: hsl(0,0%,20%);
}
#dash-version:before {
content: "\f126";
}
#dash-cpu:before, #dash-memused:before {
content: "\f2db";
}
#dash-ram:before {
content: "\f233";
}
#dash-totonline:before, #dash-gonline:before, #dash-uonline:before {
content: "\f007";
}
#dash-reqs:before {
content: "\f080";
}
#dash-postsperday:before, #dash-topicsperday:before {
content: "\f27b";
}
#panel_debug .grid_stat:not(.grid_stat_head) {
margin-bottom: 8px;
}
.debug_page.colstack_right .colstack_sub_head {
margin-top: 6px;
}
.complex_rowlist { .complex_rowlist {
background-color: inherit !important; background-color: inherit !important;

View File

@ -1399,9 +1399,9 @@ input[type=checkbox]:checked + label .sel {
} }
} }
@media(max-width: 850px) { {{/**@media(max-width: 850px) {
/**/ //
} }**/}}
@media(min-width: 1010px) { @media(min-width: 1010px) {
#container { #container {
@ -1412,11 +1412,10 @@ input[type=checkbox]:checked + label .sel {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.footBlock { .footBlock, .footer {
display: flex; display: flex;
} }
.footer { .footer {
display: flex;
flex-direction: column; flex-direction: column;
} }

View File

@ -1026,21 +1026,6 @@ blockquote:first-child {
background-color: var(--main-block-color); background-color: var(--main-block-color);
} }
#panel_dashboard_right .colstack_head .rowitem {
padding: 10px;
}
#panel_dashboard_right .colstack_head .rowitem h1 {
font-size: 15px;
margin-left: auto;
}
#panel_dashboard_right .colstack_head a {
text-align: center;
width: 100%;
display: block;
font-size: 15px;
}
@media(max-width: 935px) { @media(max-width: 935px) {
.simple .user_tag { .simple .user_tag {
display: none; display: none;

View File

@ -43,6 +43,21 @@
content: "{{lang "panel_delete_button_text" . }}"; content: "{{lang "panel_delete_button_text" . }}";
} }
#panel_dashboard_right .colstack_head .rowitem {
padding: 10px;
}
#panel_dashboard_right .colstack_head .rowitem h1, #panel_dashboard_right .colstack_sub_head .rowitem h2 {
font-size: 15px;
margin-left: auto;
margin-right: auto;
}
#panel_dashboard_right .colstack_head a, #panel_dashboard_right .colstack_sub_head a {
text-align: center;
width: 100%;
display: block;
font-size: 15px;
}
#panel_forums .rowitem { #panel_forums .rowitem {
display: flex; display: flex;
} }