From 671f7a05a633a78f2eaa51eeb2a405fe29873d97 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Sat, 6 Feb 2021 21:14:57 +0500 Subject: [PATCH] Make the relative hyphae block look nicer a little --- templates/asset.qtpl.go | 18 +++- templates/default.css | 18 +++- templates/readers.qtpl | 5 +- templates/readers.qtpl.go | 221 +++++++++++++++++++------------------- tree/tree.go | 198 +++++++++++++++++++--------------- 5 files changed, 251 insertions(+), 209 deletions(-) diff --git a/templates/asset.qtpl.go b/templates/asset.qtpl.go index af57b53..9f26407 100644 --- a/templates/asset.qtpl.go +++ b/templates/asset.qtpl.go @@ -39,7 +39,6 @@ header { width: 100%; margin-bottom: 1rem; } .hypha-tabs__selection { display: inline-block; padding: .25rem; font-weight: bold; } .relative-hyphae { margin-top: .5rem; } -.relative-hyphae > ul { margin: 0; padding: .5rem 1rem; } .relative-hyphae li { list-style-type: none; } @media screen and (max-width: 800px) { @@ -164,6 +163,19 @@ table { border: #ddd 1px solid; border-radius: .25rem; min-width: 4rem; } td { padding: .25rem; } caption { caption-side: top; font-size: small; } +.navitree { padding: 0; margin: 0; } +.navitree__trunk ul { padding-left: 1rem; } +.navitree > .navitree__trunk > ul { padding-left: 2rem; } +.navitree__entry { } +.navitree > .navitree__entry > a::before { display: inline-block; width: .5rem; color: #999; margin: 0 .25rem; } +.navitree > .navitree__entry_infertile > a::before { content: " "} /* nbsp, careful */ +.navitree > .navitree__entry_fertile > a::before { content: "▸"} +.navitree__trunk { border-left: 1px #999 solid; } +.navitree > .navitree__trunk { border-left: none; } +.navitree > .navitree__trunk > a { font-weight: bold; } +.navitree__link { text-decoration: none; display: block; padding: .25rem; } +.navitree__link:hover { background-color: #eee; } + /* Color stuff */ /* Lighter stuff #eee */ article code, @@ -210,10 +222,6 @@ blockquote { border-left: 4px black solid; } .upload-amnt { border: #eee 1px solid; } td { border: #ddd 1px solid; } -.navitree__node { padding-left: 1rem; } -.navitree__link { text-decoration: none; display: block; } -.navitree__link:hover { background-color: #eee; } - /* Dark theme! */ @media (prefers-color-scheme: dark) { html { background: #222; color: #ddd; } diff --git a/templates/default.css b/templates/default.css index c73c5a1..6ab2d95 100644 --- a/templates/default.css +++ b/templates/default.css @@ -14,7 +14,6 @@ header { width: 100%; margin-bottom: 1rem; } .hypha-tabs__selection { display: inline-block; padding: .25rem; font-weight: bold; } .relative-hyphae { margin-top: .5rem; } -.relative-hyphae > ul { margin: 0; padding: .5rem 1rem; } .relative-hyphae li { list-style-type: none; } @media screen and (max-width: 800px) { @@ -139,6 +138,19 @@ table { border: #ddd 1px solid; border-radius: .25rem; min-width: 4rem; } td { padding: .25rem; } caption { caption-side: top; font-size: small; } +.navitree { padding: 0; margin: 0; } +.navitree__trunk ul { padding-left: 1rem; } +.navitree > .navitree__trunk > ul { padding-left: 2rem; } +.navitree__entry { } +.navitree > .navitree__entry > a::before { display: inline-block; width: .5rem; color: #999; margin: 0 .25rem; } +.navitree > .navitree__entry_infertile > a::before { content: " "} /* nbsp, careful */ +.navitree > .navitree__entry_fertile > a::before { content: "▸"} +.navitree__trunk { border-left: 1px #999 solid; } +.navitree > .navitree__trunk { border-left: none; } +.navitree > .navitree__trunk > a { font-weight: bold; } +.navitree__link { text-decoration: none; display: block; padding: .25rem; } +.navitree__link:hover { background-color: #eee; } + /* Color stuff */ /* Lighter stuff #eee */ article code, @@ -185,10 +197,6 @@ blockquote { border-left: 4px black solid; } .upload-amnt { border: #eee 1px solid; } td { border: #ddd 1px solid; } -.navitree__node { padding-left: 1rem; } -.navitree__link { text-decoration: none; display: block; } -.navitree__link:hover { background-color: #eee; } - /* Dark theme! */ @media (prefers-color-scheme: dark) { html { background: #222; color: #ddd; } diff --git a/templates/readers.qtpl b/templates/readers.qtpl index 8c06d2f..57319b3 100644 --- a/templates/readers.qtpl +++ b/templates/readers.qtpl @@ -1,6 +1,7 @@ {% import "net/http" %} {% import "path" %} {% import "github.com/bouncepaw/mycorrhiza/user" %} +{% import "github.com/bouncepaw/mycorrhiza/util" %} {% func HistoryHTML(rq *http.Request, hyphaName, list string) %} {%= navHTML(rq, hyphaName, "history") %} @@ -43,10 +44,10 @@ If `contents` == "", a helpful message is shown instead.
{% if prevHyphaName != "" %} - + {% endif %} {% if nextHyphaName != "" %} - + {% endif %}
{% if u := user.FromRequest(rq); !user.AuthUsed || u.Group != "anon" %} diff --git a/templates/readers.qtpl.go b/templates/readers.qtpl.go index ab43ebe..812e65b 100644 --- a/templates/readers.qtpl.go +++ b/templates/readers.qtpl.go @@ -13,246 +13,249 @@ import "path" //line templates/readers.qtpl:3 import "github.com/bouncepaw/mycorrhiza/user" -//line templates/readers.qtpl:5 +//line templates/readers.qtpl:4 +import "github.com/bouncepaw/mycorrhiza/util" + +//line templates/readers.qtpl:6 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) -//line templates/readers.qtpl:5 +//line templates/readers.qtpl:6 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) -//line templates/readers.qtpl:5 +//line templates/readers.qtpl:6 func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) { -//line templates/readers.qtpl:5 +//line templates/readers.qtpl:6 qw422016.N().S(` `) -//line templates/readers.qtpl:6 +//line templates/readers.qtpl:7 streamnavHTML(qw422016, rq, hyphaName, "history") -//line templates/readers.qtpl:6 +//line templates/readers.qtpl:7 qw422016.N().S(`

History of `) -//line templates/readers.qtpl:10 +//line templates/readers.qtpl:11 qw422016.E().S(hyphaName) -//line templates/readers.qtpl:10 +//line templates/readers.qtpl:11 qw422016.N().S(`

`) -//line templates/readers.qtpl:11 +//line templates/readers.qtpl:12 qw422016.N().S(list) -//line templates/readers.qtpl:11 +//line templates/readers.qtpl:12 qw422016.N().S(`
`) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 } -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) { -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 StreamHistoryHTML(qw422016, rq, hyphaName, list) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 qt422016.ReleaseWriter(qw422016) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 } -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 func HistoryHTML(rq *http.Request, hyphaName, list string) string { -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 qb422016 := qt422016.AcquireByteBuffer() -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 WriteHistoryHTML(qb422016, rq, hyphaName, list) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 qs422016 := string(qb422016.B) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 qt422016.ReleaseByteBuffer(qb422016) -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 return qs422016 -//line templates/readers.qtpl:15 +//line templates/readers.qtpl:16 } -//line templates/readers.qtpl:17 +//line templates/readers.qtpl:18 func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, revHash string) { -//line templates/readers.qtpl:17 +//line templates/readers.qtpl:18 qw422016.N().S(` `) -//line templates/readers.qtpl:18 +//line templates/readers.qtpl:19 streamnavHTML(qw422016, rq, hyphaName, "revision", revHash) -//line templates/readers.qtpl:18 +//line templates/readers.qtpl:19 qw422016.N().S(`

Please note that viewing binary parts of hyphae is not supported in history for now.

`) -//line templates/readers.qtpl:23 +//line templates/readers.qtpl:24 qw422016.N().S(naviTitle) -//line templates/readers.qtpl:23 +//line templates/readers.qtpl:24 qw422016.N().S(` `) -//line templates/readers.qtpl:24 +//line templates/readers.qtpl:25 qw422016.N().S(contents) -//line templates/readers.qtpl:24 +//line templates/readers.qtpl:25 qw422016.N().S(`
`) -//line templates/readers.qtpl:27 +//line templates/readers.qtpl:28 streamrelativeHyphae(qw422016, relatives) -//line templates/readers.qtpl:27 +//line templates/readers.qtpl:28 qw422016.N().S(`
`) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 } -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, revHash string) { -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 StreamRevisionHTML(qw422016, rq, hyphaName, naviTitle, contents, relatives, revHash) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 qt422016.ReleaseWriter(qw422016) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 } -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 func RevisionHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, revHash string) string { -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 qb422016 := qt422016.AcquireByteBuffer() -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 WriteRevisionHTML(qb422016, rq, hyphaName, naviTitle, contents, relatives, revHash) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 qs422016 := string(qb422016.B) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 qt422016.ReleaseByteBuffer(qb422016) -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 return qs422016 -//line templates/readers.qtpl:29 +//line templates/readers.qtpl:30 } // If `contents` == "", a helpful message is shown instead. -//line templates/readers.qtpl:32 +//line templates/readers.qtpl:33 func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) { -//line templates/readers.qtpl:32 +//line templates/readers.qtpl:33 qw422016.N().S(` `) -//line templates/readers.qtpl:33 +//line templates/readers.qtpl:34 streamnavHTML(qw422016, rq, hyphaName, "page") -//line templates/readers.qtpl:33 +//line templates/readers.qtpl:34 qw422016.N().S(`
`) -//line templates/readers.qtpl:37 +//line templates/readers.qtpl:38 qw422016.N().S(naviTitle) -//line templates/readers.qtpl:37 +//line templates/readers.qtpl:38 qw422016.N().S(` `) -//line templates/readers.qtpl:38 +//line templates/readers.qtpl:39 if contents == "" { -//line templates/readers.qtpl:38 +//line templates/readers.qtpl:39 qw422016.N().S(`

This hypha has no text. Why not create it?

`) -//line templates/readers.qtpl:40 +//line templates/readers.qtpl:41 } else { -//line templates/readers.qtpl:40 +//line templates/readers.qtpl:41 qw422016.N().S(` `) -//line templates/readers.qtpl:41 +//line templates/readers.qtpl:42 qw422016.N().S(contents) -//line templates/readers.qtpl:41 +//line templates/readers.qtpl:42 qw422016.N().S(` `) -//line templates/readers.qtpl:42 +//line templates/readers.qtpl:43 } -//line templates/readers.qtpl:42 +//line templates/readers.qtpl:43 qw422016.N().S(`
`) -//line templates/readers.qtpl:45 +//line templates/readers.qtpl:46 if prevHyphaName != "" { -//line templates/readers.qtpl:45 +//line templates/readers.qtpl:46 qw422016.N().S(` - `) -//line templates/readers.qtpl:47 +//line templates/readers.qtpl:48 } -//line templates/readers.qtpl:47 +//line templates/readers.qtpl:48 qw422016.N().S(` `) -//line templates/readers.qtpl:48 +//line templates/readers.qtpl:49 if nextHyphaName != "" { -//line templates/readers.qtpl:48 +//line templates/readers.qtpl:49 qw422016.N().S(` - `) -//line templates/readers.qtpl:50 +//line templates/readers.qtpl:51 } -//line templates/readers.qtpl:50 +//line templates/readers.qtpl:51 qw422016.N().S(`
`) -//line templates/readers.qtpl:52 +//line templates/readers.qtpl:53 if u := user.FromRequest(rq); !user.AuthUsed || u.Group != "anon" { -//line templates/readers.qtpl:52 +//line templates/readers.qtpl:53 qw422016.N().S(`
`) -//line templates/readers.qtpl:56 +//line templates/readers.qtpl:57 if hasAmnt { -//line templates/readers.qtpl:56 +//line templates/readers.qtpl:57 qw422016.N().S(` Unattach current attachment? `) -//line templates/readers.qtpl:58 +//line templates/readers.qtpl:59 } -//line templates/readers.qtpl:58 +//line templates/readers.qtpl:59 qw422016.N().S(`
@@ -260,43 +263,43 @@ func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navi
`) -//line templates/readers.qtpl:64 +//line templates/readers.qtpl:65 } -//line templates/readers.qtpl:64 +//line templates/readers.qtpl:65 qw422016.N().S(`
`) -//line templates/readers.qtpl:66 +//line templates/readers.qtpl:67 streamrelativeHyphae(qw422016, relatives) -//line templates/readers.qtpl:66 +//line templates/readers.qtpl:67 qw422016.N().S(`
`) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 } -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 func WritePageHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) { -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 StreamPageHTML(qw422016, rq, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName, hasAmnt) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 qt422016.ReleaseWriter(qw422016) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 } -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) string { -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 qb422016 := qt422016.AcquireByteBuffer() -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 WritePageHTML(qb422016, rq, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName, hasAmnt) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 qs422016 := string(qb422016.B) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 qt422016.ReleaseByteBuffer(qb422016) -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 return qs422016 -//line templates/readers.qtpl:68 +//line templates/readers.qtpl:69 } diff --git a/tree/tree.go b/tree/tree.go index 476b540..45046db 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -9,100 +9,122 @@ import ( "github.com/bouncepaw/mycorrhiza/util" ) -// If Name == "", the tree is empty. -type tree struct { - name string - exists bool - prevSibling string - nextSibling string - siblings []string - descendants []*tree - root bool - hyphaIterator func(func(string)) +type sibling struct { + name string + hasChildren bool +} + +func (s *sibling) checkThisChild(hyphaName string) { + if !s.hasChildren && path.Dir(hyphaName) == s.name { + s.hasChildren = true + } +} + +func (s *sibling) asHTML() string { + class := "navitree__entry navitree__sibling" + if s.hasChildren { + class += " navitree__sibling_fertile navitree__entry_fertile" + } else { + class += " navitree__sibling_infertile navitree__entry_infertile" + } + return fmt.Sprintf( + `
  • %s
  • `, + class, + s.name, + util.BeautifulName(path.Base(s.name)), + ) +} + +type mainFamilyMember struct { + name string + children []*mainFamilyMember +} + +func (m *mainFamilyMember) checkThisChild(hyphaName string) (adopted bool) { + if path.Dir(hyphaName) == m.name { + m.children = append(m.children, &mainFamilyMember{ + name: hyphaName, + children: make([]*mainFamilyMember, 0), + }) + return true + } + return false +} + +func (m *mainFamilyMember) asHTML() string { + if len(m.children) == 0 { + return fmt.Sprintf(`
  • %s
  • `, m.name, util.BeautifulName(path.Base(m.name))) + } + sort.Slice(m.children, func(i, j int) bool { + return m.children[i].name < m.children[j].name + }) + html := fmt.Sprintf(`
  • %s
  • ` +} + +func mainFamilyFromPool(hyphaName string, subhyphaePool map[string]bool) *mainFamilyMember { + var ( + nestLevel = strings.Count(hyphaName, "/") + adopted = make([]*mainFamilyMember, 0) + ) + for subhyphaName, _ := range subhyphaePool { + subnestLevel := strings.Count(subhyphaName, "/") + if subnestLevel-1 == nestLevel && path.Dir(subhyphaName) == hyphaName { + delete(subhyphaePool, subhyphaName) + adopted = append(adopted, mainFamilyFromPool(subhyphaName, subhyphaePool)) + } + } + return &mainFamilyMember{name: hyphaName, children: adopted} } // Tree generates a tree for `hyphaName` as html and returns next and previous hyphae if any. func Tree(hyphaName string, hyphaIterator func(func(string))) (html, prev, next string) { - t := &tree{name: hyphaName, root: true, hyphaIterator: hyphaIterator} - t.fill() - return t.asHtml(), util.BeautifulName(t.prevSibling), util.BeautifulName(t.nextSibling) -} - -// subtree adds a descendant tree to `t` and returns that tree. -func (t *tree) fork(descendantName string) *tree { - subt := &tree{ - name: descendantName, - root: false, - hyphaIterator: t.hyphaIterator, - } - t.descendants = append(t.descendants, subt) - return subt -} - -// Compare current prev next hyphae and decide if any of them should be set to `name2`. -func (t *tree) prevNextDetermine(name2 string) { - if name2 < t.name && (name2 > t.prevSibling || t.prevSibling == "") { - t.prevSibling = name2 - } else if name2 > t.name && (name2 < t.nextSibling || t.nextSibling == "") { - t.nextSibling = name2 - } -} - -// Compares names and does something with them, may generate a subtree. -func (t *tree) compareNamesAndAppend(name2 string) { - switch { - case t.name == name2: - t.exists = true - case t.root && path.Dir(t.name) == path.Dir(name2): - t.prevNextDetermine(name2) - t.siblings = append(t.siblings, name2) - case t.name == path.Dir(name2): - t.fork(name2).fill() - } -} - -// Fills t.siblings and t.descendants, sorts them and does the same to the descendants. -func (t *tree) fill() { - t.hyphaIterator(func(hyphaName string) { - t.compareNamesAndAppend(hyphaName) - }) - sort.Strings(t.siblings) - sort.Slice(t.descendants, func(i, j int) bool { - return t.descendants[i].name < t.descendants[j].name - }) -} - -// asHtml returns HTML representation of a tree. -// It applies itself recursively on the tree's children. -func (t *tree) asHtml() (html string) { - if t.root { - html += navitreeEntry(t.name, "navitree__pagename") - } else { - html += navitreeEntry(t.name, "navitree__name") - } - - for _, subtree := range t.descendants { - html += subtree.asHtml() - } - - if t.root { - for _, siblingName := range t.siblings { - html += navitreeEntry(siblingName, "navitree__sibling") + var ( + // One of the siblings is the hypha with name `hyphaName` + siblings = findSiblings(hyphaName, hyphaIterator) + subhyphaePool = make(map[string]bool) + I int + ) + hyphaIterator(func(otherHyphaName string) { + for _, s := range siblings { + s.checkThisChild(otherHyphaName) } + if strings.HasPrefix(otherHyphaName, hyphaName+"/") { + subhyphaePool[otherHyphaName] = true + } + }) + for i, s := range siblings { + if s.name == hyphaName { + I = i + break + } + html += s.asHTML() } - - return `` + html += mainFamilyFromPool(hyphaName, subhyphaePool).asHTML() + for _, s := range siblings[I+1:] { + html += s.asHTML() + } + if I != 0 { + prev = siblings[I-1].name + } + if I != len(siblings)-1 { + next = siblings[I+1].name + } + return fmt.Sprintf(``, html), prev, next } -// Strip hypha name from all ancestor names, replace _ with spaces, title case -func beautifulName(uglyName string) string { - return strings.Title(strings.ReplaceAll(path.Base(uglyName), "_", " ")) -} - -// navitreeEntry is a small utility function that makes generating html easier. -func navitreeEntry(name, class string) string { - return fmt.Sprintf(`
  • - %s -
  • -`, class, name, beautifulName(name)) +func findSiblings(hyphaName string, hyphaIterator func(func(string))) []*sibling { + siblings := []*sibling{&sibling{name: hyphaName, hasChildren: true}} + hyphaIterator(func(otherHyphaName string) { + if path.Dir(hyphaName) == path.Dir(otherHyphaName) && hyphaName != otherHyphaName { + siblings = append(siblings, &sibling{name: otherHyphaName, hasChildren: false}) + } + }) + sort.Slice(siblings, func(i, j int) bool { + return siblings[i].name < siblings[j].name + }) + return siblings }