1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +00:00

Reimplement view, create render module, add favicon

This commit is contained in:
Timur Ismagilov 2020-06-25 22:18:50 +05:00
parent 7202cf5ab7
commit 472aea92f8
47 changed files with 1222 additions and 119 deletions

View File

@ -26,12 +26,13 @@ var (
TitleTemplate = `%s`
GenericErrorMsg = `<b>Sorry, something went wrong</b>`
SiteTitle = `MycorrhizaWiki`
Theme = `default-light`
)
func InitConfig(wd string) bool {
log.Println("WikiDir is", wd)
WikiDir = wd
TemplatesDir = filepath.Join(filepath.Dir(WikiDir), "templates")
TemplatesDir = "Templates"
configJsonPath = filepath.Join(filepath.Dir(WikiDir), "config.json")
if _, err := os.Stat(configJsonPath); os.IsNotExist(err) {
@ -53,6 +54,7 @@ func readConfig() bool {
cfg := struct {
Address string `json:"address"`
Theme string `json:"theme"`
SiteTitle string `json:"site-title"`
TitleTemplates struct {
EditHypha string `json:"edit-hypha"`
@ -67,6 +69,7 @@ func readConfig() bool {
}
Address = cfg.Address
Theme = cfg.Theme
SiteTitle = cfg.SiteTitle
TitleEditTemplate = cfg.TitleTemplates.EditHypha
TitleTemplate = cfg.TitleTemplates.ViewHypha

View File

@ -14,15 +14,16 @@ type Storage struct {
root string
}
var Hs *Storage
// InitStorage initiates filesystem-based hypha storage. It has to be called after configuration was inited.
func InitStorage() *Storage {
s := &Storage{
func InitStorage() {
Hs = &Storage{
paths: make(map[string]string),
root: cfg.WikiDir,
}
s.indexHyphae(s.root)
log.Printf("Indexed %v hyphae\n", len(s.paths))
return s
Hs.indexHyphae(Hs.root)
log.Printf("Indexed %v hyphae\n", len(Hs.paths))
}
// hyphaName gets name of a hypha by stripping path to the hypha in `fullPath`

91
fs/genealogy.go Normal file
View File

@ -0,0 +1,91 @@
package fs
import (
"fmt"
"log"
"path/filepath"
"sort"
"strings"
)
func (s *Storage) RenderHypha(h *Hypha) {
}
// If Name == "", the tree is empty.
type Tree struct {
Name string
Ancestors []string
Siblings []string
Descendants []*Tree
Root bool
}
// GetTree generates a Tree for the given hypha name.
// It can also generate trees for non-existent hyphae, that's why we use `name string` instead of making it a method on `Hypha`.
// In `root` is `false`, siblings will not be fetched.
func (s *Storage) GetTree(name string, root bool) *Tree {
t := &Tree{Name: name, Root: root}
for hyphaName, _ := range s.paths {
s.compareNamesAndAppend(t, hyphaName)
}
sort.Slice(t.Ancestors, func(i, j int) bool {
return strings.Count(t.Ancestors[i], "/") < strings.Count(t.Ancestors[j], "/")
})
sort.Strings(t.Siblings)
sort.Slice(t.Descendants, func(i, j int) bool {
a := t.Descendants[i].Name
b := t.Descendants[j].Name
return len(a) < len(b)
})
log.Printf("Generate tree for %v: %v %v\n", t.Name, t.Ancestors, t.Siblings)
return t
}
// Compares names appends name2 to an array of `t`:
func (s *Storage) compareNamesAndAppend(t *Tree, name2 string) {
switch {
case t.Name == name2:
case strings.HasPrefix(t.Name, name2):
t.Ancestors = append(t.Ancestors, name2)
case t.Root && (strings.Count(t.Name, "/") == strings.Count(name2, "/") &&
(filepath.Dir(t.Name) == filepath.Dir(name2))):
t.Siblings = append(t.Siblings, name2)
case strings.HasPrefix(name2, t.Name):
t.Descendants = append(t.Descendants, s.GetTree(name2, false))
}
}
// asHtml returns HTML representation of a tree.
// It recursively itself on the tree's children.
// TODO: redo with templates. I'm not in mood for it now.
func (t *Tree) AsHtml() (html string) {
if t.Name == "" {
return ""
}
html += `<ul class="navitree__node">`
if t.Root {
for _, ancestor := range t.Ancestors {
html += navitreeEntry(ancestor, "navitree__ancestor")
}
for _, siblingName := range t.Siblings {
html += navitreeEntry(siblingName, "navitree__sibling")
}
}
html += navitreeEntry(t.Name, "navitree__name")
for _, subtree := range t.Descendants {
html += subtree.AsHtml()
}
html += `</ul>`
return html
}
// navitreeEntry is a small utility function that makes generating html easier.
// Someone please redo it in templates.
func navitreeEntry(name, class string) string {
return fmt.Sprintf(`<li class="navitree__entry">
<a class="navitree__link %s" href="/%s">%s</a>
</li>
`, class, name, filepath.Base(name))
}

View File

@ -23,6 +23,10 @@ type Hypha struct {
actual *Revision `json:"-"`
}
func (h *Hypha) TextPath() string {
return h.actual.TextPath
}
func (s *Storage) Open(name string) (*Hypha, error) {
h := &Hypha{
Exists: true,
@ -98,7 +102,11 @@ func (h *Hypha) NewestId() string {
}
func (h *Hypha) PlainLog(s string) {
log.Println(h.FullName, h.actual.Id, s)
if h.Exists {
log.Println(h.FullName, h.actual.Id, s)
} else {
log.Println("nonexistent", h.FullName, s)
}
}
func (h *Hypha) mimeTypeForActionRaw() string {
@ -122,8 +130,7 @@ func (h *Hypha) asHtml() (string, error) {
`
// What about using <figure>?
if h.hasBinaryData() {
ret += fmt.Sprintf(`<img src="/%s?action=getBinary&rev=%d" cla
ss="page__amnt"/>`, rev.FullName, rev.Id)
ret += fmt.Sprintf(`<img src="/%s?action=binary&rev=%d" class="page__amnt"/>`, rev.FullName, rev.Id)
}
contents, err := ioutil.ReadFile(rev.TextPath)
@ -162,9 +169,9 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) {
h.PlainLog("Serving raw text")
}
// ActionGetBinary is used with `?action=getBinary`.
// ActionBinary is used with `?action=binary`.
// It writes contents of binary content file.
func (h *Hypha) ActionGetBinary(w http.ResponseWriter) {
func (h *Hypha) ActionBinary(w http.ResponseWriter) {
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
if err != nil {
log.Fatal(err)
@ -190,3 +197,26 @@ func (h *Hypha) ActionZen(w http.ResponseWriter) {
w.Write([]byte(html))
h.PlainLog("Rendering zen")
}
// ActionView is used with `?action=view` or no action at all.
// It renders the page, the layout and everything else.
func (h *Hypha) ActionView(w http.ResponseWriter, renderExists, renderNotExists func(string, string) string) {
var html string
var err error
if h.Exists {
html, err = h.asHtml()
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
if h.Exists {
w.Write([]byte(renderExists(h.FullName, html)))
} else {
w.Write([]byte(renderNotExists(h.FullName, "")))
}
h.PlainLog("Rendering hypha view")
}

View File

@ -11,6 +11,7 @@ import (
// "time"
"github.com/bouncepaw/mycorrhiza/fs"
"github.com/bouncepaw/mycorrhiza/render"
"github.com/gorilla/mux"
)
@ -19,7 +20,7 @@ import (
// Boilerplate code present in many handlers. Good to have it.
func HandlerBase(w http.ResponseWriter, rq *http.Request) (*fs.Hypha, bool) {
vars := mux.Vars(rq)
h, err := hs.Open(vars["hypha"])
h, err := fs.Hs.Open(vars["hypha"])
if err != nil {
log.Println(err)
return nil, false
@ -40,10 +41,10 @@ func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
}
}
func HandlerGetBinary(w http.ResponseWriter, rq *http.Request) {
log.Println("?action=getBinary")
func HandlerBinary(w http.ResponseWriter, rq *http.Request) {
log.Println("?action=binary")
if h, ok := HandlerBase(w, rq); ok {
h.ActionGetBinary(w)
h.ActionBinary(w)
}
}
@ -53,13 +54,13 @@ func HandlerZen(w http.ResponseWriter, rq *http.Request) {
}
}
/*
func HandlerView(w http.ResponseWriter, rq *http.Request) {
if h, ok := HandlerBase(w, rq); ok {
h.ActionView(w, HyphaPage)
h.ActionView(w, render.HyphaPage, render.Hypha404)
}
}
/*
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
ActionEdit(vars["hypha"], w)

28
main.go
View File

@ -22,8 +22,6 @@ func RevInMap(m map[string]string) string {
return "0"
}
var hs *fs.Storage
func main() {
if len(os.Args) == 1 {
panic("Expected a root wiki pages directory")
@ -36,15 +34,19 @@ func main() {
log.Println("Welcome to MycorrhizaWiki α")
cfg.InitConfig(wikiDir)
log.Println("Indexing hyphae...")
hs = fs.InitStorage()
fs.InitStorage()
// Start server code. See handlers.go for handlers' implementations.
r := mux.NewRouter()
r.Queries("action", "getBinary", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
HandlerFunc(HandlerGetBinary)
r.Queries("action", "getBinary").Path(cfg.HyphaUrl).
HandlerFunc(HandlerGetBinary)
r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
http.ServeFile(w, rq, filepath.Join(filepath.Dir(cfg.WikiDir), "favicon.ico"))
})
r.Queries("action", "binary", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
HandlerFunc(HandlerBinary)
r.Queries("action", "binary").Path(cfg.HyphaUrl).
HandlerFunc(HandlerBinary)
r.Queries("action", "raw", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
HandlerFunc(HandlerRaw)
@ -56,12 +58,12 @@ func main() {
r.Queries("action", "zen").Path(cfg.HyphaUrl).
HandlerFunc(HandlerZen)
/*
r.Queries("action", "view", "rev", revQuery).Path(hyphaUrl).
HandlerFunc(HandlerView)
r.Queries("action", "view").Path(hyphaUrl).
HandlerFunc(HandlerView)
r.Queries("action", "view", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
HandlerFunc(HandlerView)
r.Queries("action", "view").Path(cfg.HyphaUrl).
HandlerFunc(HandlerView)
/*
r.Queries("action", "edit").Path(hyphaUrl).
HandlerFunc(HandlerEdit)
@ -69,7 +71,7 @@ func main() {
HandlerFunc(HandlerUpdate)
*/
// r.HandleFunc(hyphaUrl, HandlerView)
r.HandleFunc(cfg.HyphaUrl, HandlerView)
// Debug page that renders all hyphae.
// TODO: make it redirect to home page.

101
render/render.go Normal file
View File

@ -0,0 +1,101 @@
package render
import (
"bytes"
"fmt"
"path"
"text/template"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/fs"
)
// EditHyphaPage returns HTML page of hypha editor.
func EditHyphaPage(name, textMime, content, tags string) string {
page := map[string]string{
"Text": content,
"TextMime": textMime,
"Name": name,
"Tags": tags,
}
keys := map[string]string{
"Title": fmt.Sprintf(cfg.TitleEditTemplate, name),
"Header": renderFromString(name, "Hypha/edit/header.html"),
"Sidebar": renderFromMap(page, "Hypha/edit/sidebar.html"),
}
return renderBase(renderFromMap(page, "Hypha/edit/index.html"), keys)
}
// Hypha404 returns 404 page for nonexistent page.
func Hypha404(name, _ string) string {
return HyphaGeneric(name, name, "Hypha/view/404.html")
}
// HyphaPage returns HTML page of hypha viewer.
func HyphaPage(name, content string) string {
return HyphaGeneric(name, content, "Hypha/view/index.html")
}
// HyphaGeneric is used when building renderers for all types of hypha pages
func HyphaGeneric(name string, content, templatePath string) string {
var sidebar string
if aside := renderFromMap(map[string]string{
"Tree": fs.Hs.GetTree(name, true).AsHtml(),
}, "Hypha/view/sidebar.html"); aside != "" {
sidebar = aside
}
keys := map[string]string{
"Title": fmt.Sprintf(cfg.TitleTemplate, name),
"Sidebar": sidebar,
}
return renderBase(renderFromString(content, templatePath), keys)
}
// renderBase collects and renders page from base template
// Args:
// content: string or pre-rendered template
// keys: map with replaced standart fields
func renderBase(content string, keys map[string]string) string {
page := map[string]string{
"Title": cfg.SiteTitle,
"Main": "",
"SiteTitle": cfg.SiteTitle,
}
for key, val := range keys {
page[key] = val
}
page["Main"] = content
return renderFromMap(page, "base.html")
}
// renderFromMap applies `data` map to template in `templatePath` and returns the result.
func renderFromMap(data map[string]string, templatePath string) string {
hyphPath := path.Join(cfg.TemplatesDir, cfg.Theme, templatePath)
h, _ := fs.Hs.Open(hyphPath)
h.OnRevision("0")
tmpl, err := template.ParseFiles(h.TextPath())
if err != nil {
return err.Error()
}
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, data); err != nil {
return err.Error()
}
return buf.String()
}
// renderFromMap applies `data` string to template in `templatePath` and returns the result.
func renderFromString(data string, templatePath string) string {
hyphPath := path.Join(cfg.TemplatesDir, cfg.Theme, templatePath)
h, _ := fs.Hs.Open(hyphPath)
h.OnRevision("0")
tmpl, err := template.ParseFiles(h.TextPath())
if err != nil {
return err.Error()
}
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, data); err != nil {
return err.Error()
}
return buf.String()
}

View File

@ -1,5 +1,6 @@
{
"address": "127.0.0.1:1737",
"theme": "default-light",
"site-title": "🍄 MycorrhizaWiki",
"title-templates": {
"edit-hypha": "Edit %s at MycorrhizaWiki",

BIN
w/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -1 +1 @@
<h1 class="header__edit-title">Edit {{ . }}</h1>
<h1 class="header__edit-title">Edit {{ . }}</h1>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "header.html",
"comment": "Create Templates/default-dark/Hypha/edit/header.html",
"author": "",
"time": 1592996801,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,16 @@
<div class="naviwrapper">
<form class="naviwrapper__edit edit-box"
method="POST"
id="editform"
enctype="multipart/form-data"
action="?action=update">
<div class="edit-box__left">
<h4 class="">Edit box</h4>
<!-- It is important that there is no indent ↓ -->
<textarea class="edit-box__text" name="text" cols="80" rows="25">
{{ .Text }}
</textarea>
</div>
</form>
</div>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "index.html",
"comment": "Create Templates/default-dark/Hypha/edit/index.html",
"author": "",
"time": 1592996876,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,19 @@
<div style=""><h4>Text MIME-type</h4>
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
<input type="text" name="text_mime" value="{{ .TextMime }}"/>
<h4>Revision comment</h4>
<p>Please make your comment helpful</p>
<input type="text" name="comment" value="Update {{ .Name }}"/>
<h4>Edit tags</h4>
<p>Tags are separated by commas, whitespace is ignored</p>
<input type="text" name="tags" value="{{ .Tags }}"/>
<h4>Upload file</h4>
<p>If this hypha has a file like that, the text above is meant to be a description of it</p>
<input type="file" name="binary"/>
<p><input type="submit" value="update"/></p>
</div>

View File

@ -0,0 +1,19 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": [
""
],
"name": "sidebar.html",
"comment": "Create Templates/default-dark/Hypha/edit/sidebar.html",
"author": "",
"time": 1593003792,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -1,4 +1,4 @@
<h1 class="page__title">{{ . }}</h1>
<p class="msg_hypha-does-not-exist msg">
The hypha you are trying to access does not exist yet. Why not <a href="?action=edit">create</a> it?
</p>
<h1 class="page__title">{{ . }}</h1>
<p class="msg_hypha-does-not-exist msg">
The hypha you are trying to access does not exist yet. Why not <a href="?action=edit">create</a> it?
</p>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "404.html",
"comment": "Create Templates/default-light/Hypha/view/404.html",
"author": "",
"time": 1592996917,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "index.html",
"comment": "Create Templates/default-light/Hypha/view/index.html",
"author": "",
"time": 1592996954,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -1,9 +1,9 @@
<div class="hypha-actions">
<ul>
<li><a href="?action=edit">Edit</a></li>
<li><a href="?action=getBinary">Download</a></li>
<li><a href="?action=zen">Zen mode</a></li>
<li><a href="?action=raw">View raw</a></li>
</ul>
</div>
{{ .Tree }}
<div class="hypha-actions">
<ul>
<li><a href="?action=edit">Edit</a></li>
<li><a href="?action=getBinary">Download</a></li>
<li><a href="?action=zen">Zen mode</a></li>
<li><a href="?action=raw">View raw</a></li>
</ul>
</div>
{{ .Tree }}

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "sidebar.html",
"comment": "Create Templates/default-light/Hypha/view/sidebar.html",
"author": "",
"time": 1592996977,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -1,30 +1,30 @@
<html>
<head>
<title>{{ .Title }}</title>
<link rel="stylesheet" href="/sys/main.css?action=raw">
</head>
<body>
<div class="shroom">
<button class="shroom__button" id="shroomBtn"><span>🍄</span> Open</button>
</div>
<main class="main">{{ .Main }}</main>
<div class="left-panel" id="shroomburgerMenu">
<div class="left-panel__in">
<div class="shroom mushroom">
<button class="shroom__button" id="mushroomBtn"><span>🍄</span> Close</button>
</div>
<div class="left-panel__contents">
<!-- Site title is fetched from your config.json. Set your title in "site-title" field. You can add more things to the header here. -->
<header class="header">
<h1 class="header__site-title">{{ .SiteTitle }}</h1>
</header>
<aside class="sidebar">{{ .Sidebar }}</aside>
<footer class="footer">
<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p>
</footer>
</div>
</div>
</div>
<script src="/sys/main.js?action=raw"></script>
</body>
</html>
<html>
<head>
<title>{{ .Title }}</title>
<link rel="stylesheet" href="/Templates/default-dark/main.css?action=raw">
</head>
<body>
<div class="shroom">
<button class="shroom__button" id="shroomBtn"><span>🍄</span> Open</button>
</div>
<main class="main">{{ .Main }}</main>
<div class="left-panel" id="shroomburgerMenu">
<div class="left-panel__in">
<div class="shroom mushroom">
<button class="shroom__button" id="mushroomBtn"><span>🍄</span> Close</button>
</div>
<div class="left-panel__contents">
<!-- Site title is fetched from your config.json. Set your title in "site-title" field. You can add more things to the header here. -->
<header class="header">
<h1 class="header__site-title">{{ .SiteTitle }}</h1>
</header>
<aside class="sidebar">{{ .Sidebar }}</aside>
<footer class="footer">
<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p>
</footer>
</div>
</div>
</div>
<script src="/Templates/default-dark/main.js?action=raw"></script>
</body>
</html>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "base.html",
"comment": "Create Templates/default-dark/base.html",
"author": "",
"time": 1592996503,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,221 @@
*, *::before, *::after {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
font: 15px/1.5 system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', 'PT Sans', 'Roboto', 'Arial', sans-serif;
max-width: 500px;
min-height: 100%;
margin: 0 auto;
padding: 12px 24px;
background-color: #272b30;
color: #c8c8c8;
}
.msg {
background-color: #f4f4f4;
padding: 1rem;
border-radius: 1rem;
}
.shroom {
margin: 0;
}
.shroom__button {
border-radius: 1rem;
padding: 8px 16px 8px 0;
border: none;
background: #f0f2f4;
color: #444;
font: inherit;
font-size: 15px;
font-weight: 500;
text-align: left;
}
.shroom span {
margin-left: 16px;
margin-right: 8px;
font-size: 20px;
vertical-align: -0.04em;
}
.mushroom .shroom__button {
background: #44484a;
color: #dddfe4;
}
.header {
padding: 8px 0;
}
.header h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
letter-spacing: 0.02em;
color: #20ce92;
}
a {
color: #019fe3;
}
/*a:visited {
color: #44a;
}*/
h1, h2, h3, h4, h5, h6 {
margin: 0.5em 0 0.25em;
}
.page {
font-size: 16px;
line-height: 1.666;
max-width: 40em;
hyphens: auto;
}
.page pre {
white-space: break-spaces;
}
.page__amnt {
max-width: 100%;
}
.page__title {
font-family: 'PT Serif', 'Georgia', serif;
font-size: 36px;
font-weight: normal;
color: #20ce92;
}
.edit-box {
display: grid;
grid-template-columns: 7fr 5fr;
}
.edit-box .naviwrapper__buttons {
grid-column: 1;
grid-row: 2;
}
.edit-box__left { grid-column: 1; grid-row: 2 }
.edit-box__right { grid-column: 2; grid-row: 1 / span 2; padding-right: 16px }
.edit-box__text {
border-radius: 1rem;
color: #c8c8c8;
padding: 16px;
background-color: rgba(255,255,255,.05);
}
footer {
padding: 1em 0;
font-size: 12px;
color: #7a8288;
}
footer a, footer a:visited {
color: #7a8288;
}
.left-panel {
display: none;
}
.left-panel.active {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fafafa;
}
.left-panel.active .sidebar {
background: #fff;
}
.left-panel__in {
width: 100%;
height: 100%;
max-width: 500px;
margin: 0 auto;
padding: 12px 24px;
}
.left-panel__contents {
width: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
}
.left-panel .shroom {
margin-bottom: 16px;
}
@media (min-width: 700px) {
body {
max-width: 1200px;
padding: 8px 16px;
padding-right: 274px;
}
.shroom {
display: none;
}
.page {
font-size: 18px;
}
.left-panel {
display: block;
position: fixed;
top: 0;
bottom: 0;
min-width: 274px;
right: 0;
}
.left-panel__contents {
height: 100%;
}
}
.sidebar {
padding: 16px;
border-radius: 1rem;
background-color: rgba(255,255,255,.05);
}
.hypha-actions ul {
margin: 0;
padding: 0;
}
.hypha-actions li {
list-style: none;
}
.hypha-actions a {
display: block;
padding: 6px 16px;
font: inherit;
text-decoration: none;
color: #7a8288;
transition: 0.1s background;
}
aside .hypha-actions a:hover {
background-color: #272b30;
color: #73ca73;
}

View File

@ -0,0 +1,19 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": [
""
],
"name": "main.css",
"comment": "Update sys/main.css",
"author": "",
"time": 1592666188,
"text_mime": "text/css",
"binary_mime": "",
"text_name": "1.css",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,7 @@
var menu = document.getElementById('shroomburgerMenu');
document.getElementById('shroomBtn').addEventListener('click', function() {
menu.classList.add('active');
});
document.getElementById('mushroomBtn').addEventListener('click', function() {
menu.classList.remove('active');
});

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "main.js",
"comment": "Update sys/main.js",
"author": "",
"time": 1592937088,
"text_mime": "text/javascript",
"binary_mime": "",
"text_name": "1.mjs",
"binary_name": ""
}
}
}

View File

@ -1,8 +1,8 @@
<html>
<head>
<title>Saved {{ .Name }}</title>
</head>
<body>
<p>Saved successfully. <a href="/{{ .Name }}">Go back</a></p>
</body>
</html>
<html>
<head>
<title>Saved {{ .Name }}</title>
</head>
<body>
<p>Saved successfully. <a href="/{{ .Name }}">Go back</a></p>
</body>
</html>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "updateOk.html",
"comment": "Create Templates/default-dark/updateOk.html",
"author": "",
"time": 1592996644,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1 @@
<h1 class="header__edit-title">Edit {{ . }}</h1>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "header.html",
"comment": "Create Templates/default-light/Hypha/edit/header.html",
"author": "",
"time": 1592996801,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -1,36 +1,36 @@
<div class="naviwrapper">
<form class="naviwrapper__edit edit-box"
method="POST"
enctype="multipart/form-data"
action="?action=update">
<div class="naviwrapper__buttons">
<input type="submit" value="update"/>
</div>
<div class="edit-box__left">
<h4>Edit box</h4>
<!-- It is important that there is no indent ↓ -->
<textarea class="edit-box__text" name="text" cols="80" rows="25">
{{ .Text }}
</textarea>
<h4>Upload file</h4>
<p>If this hypha has a file like that, the text above is meant to be a description of it</p>
<input type="file" name="binary"/>
</div>
<div class="edit-box__right">
<h4>Text MIME-type</h4>
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
<input type="text" name="text_mime" value="{{ .TextMime }}"/>
<h4>Revision comment</h4>
<p>Please make your comment helpful</p>
<input type="text" name="comment" value="Update {{ .Name }}"/>
<h4>Edit tags</h4>
<p>Tags are separated by commas, whitespace is ignored</p>
<input type="text" name="tags" value="{{ .Tags }}"/>
</div>
</form>
</div>
<div class="naviwrapper">
<form class="naviwrapper__edit edit-box"
method="POST"
enctype="multipart/form-data"
action="?action=update">
<div class="naviwrapper__buttons">
<input type="submit" value="update"/>
</div>
<div class="edit-box__left">
<h4>Edit box</h4>
<!-- It is important that there is no indent ↓ -->
<textarea class="edit-box__text" name="text" cols="80" rows="25">
{{ .Text }}
</textarea>
<h4>Upload file</h4>
<p>If this hypha has a file like that, the text above is meant to be a description of it</p>
<input type="file" name="binary"/>
</div>
<div class="edit-box__right">
<h4>Text MIME-type</h4>
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
<input type="text" name="text_mime" value="{{ .TextMime }}"/>
<h4>Revision comment</h4>
<p>Please make your comment helpful</p>
<input type="text" name="comment" value="Update {{ .Name }}"/>
<h4>Edit tags</h4>
<p>Tags are separated by commas, whitespace is ignored</p>
<input type="text" name="tags" value="{{ .Tags }}"/>
</div>
</form>
</div>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "index.html",
"comment": "Create Templates/default-light/Hypha/edit/index.html",
"author": "",
"time": 1592996876,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,4 @@
<h1 class="page__title">{{ . }}</h1>
<p class="msg_hypha-does-not-exist msg">
The hypha you are trying to access does not exist yet. Why not <a href="?action=edit">create</a> it?
</p>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "404.html",
"comment": "Create Templates/default-light/Hypha/view/404.html",
"author": "",
"time": 1592996917,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1 @@
{{ . }}

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "index.html",
"comment": "Create Templates/default-light/Hypha/view/index.html",
"author": "",
"time": 1592996954,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,9 @@
<div class="hypha-actions">
<ul>
<li><a href="?action=edit">Edit</a></li>
<li><a href="?action=getBinary">Download</a></li>
<li><a href="?action=zen">Zen mode</a></li>
<li><a href="?action=raw">View raw</a></li>
</ul>
</div>
{{ .Tree }}

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "sidebar.html",
"comment": "Create Templates/default-light/Hypha/view/sidebar.html",
"author": "",
"time": 1592996977,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,30 @@
<html>
<head>
<title>{{ .Title }}</title>
<link rel="stylesheet" href="/Templates/default-light/main.css?action=raw">
</head>
<body>
<div class="shroom">
<button class="shroom__button" id="shroomBtn"><span>🍄</span> Open</button>
</div>
<main class="main">{{ .Main }}</main>
<div class="left-panel" id="shroomburgerMenu">
<div class="left-panel__in">
<div class="shroom mushroom">
<button class="shroom__button" id="mushroomBtn"><span>🍄</span> Close</button>
</div>
<div class="left-panel__contents">
<!-- Site title is fetched from your config.json. Set your title in "site-title" field. You can add more things to the header here. -->
<header class="header">
<h1 class="header__site-title">{{ .SiteTitle }}</h1>
</header>
<aside class="sidebar">{{ .Sidebar }}</aside>
<footer class="footer">
<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p>
</footer>
</div>
</div>
</div>
<script src="/Templates/default-light/main.js?action=raw"></script>
</body>
</html>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "base.html",
"comment": "Create Templates/default-light/base.html",
"author": "",
"time": 1592996503,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,221 @@
*, *::before, *::after {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
font: 15px/1.5 system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', 'PT Sans', 'Roboto', 'Arial', sans-serif;
max-width: 500px;
min-height: 100%;
margin: 0 auto;
padding: 12px 24px;
}
.msg {
background-color: #f4f4f4;
padding: 1rem;
border-radius: 1rem;
}
.shroom {
margin: 0;
}
.shroom__button {
border-radius: 1rem;
padding: 8px 16px 8px 0;
border: none;
background: #f0f2f4;
color: #444;
font: inherit;
font-size: 15px;
font-weight: 500;
text-align: left;
}
.shroom span {
margin-left: 16px;
margin-right: 8px;
font-size: 20px;
vertical-align: -0.04em;
}
.mushroom .shroom__button {
background: #44484a;
color: #dddfe4;
}
.header {
padding: 8px 0;
}
.header h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
letter-spacing: 0.02em;
color: #222428;
}
a {
color: #44e;
}
a:visited {
color: #44a;
}
h1, h2, h3, h4, h5, h6 {
margin: 0.5em 0 0.25em;
}
.page {
font-size: 16px;
line-height: 1.666;
max-width: 40em;
hyphens: auto;
}
.page pre {
white-space: break-spaces;
}
.page__amnt {
max-width: 100%;
}
.page__title {
font-family: 'PT Serif', 'Georgia', serif;
font-size: 36px;
font-weight: normal;
}
.edit-box {
display: grid;
grid-template-columns: 7fr 5fr;
}
.edit-box .naviwrapper__buttons {
grid-column: 1;
grid-row: 2;
}
.edit-box__left { grid-column: 1; grid-row: 2 }
.edit-box__right { grid-column: 2; grid-row: 1 / span 2; padding-right: 16px }
footer {
padding: 1em 0;
font-size: 12px;
color: #888;
}
footer a, footer a:visited {
color: #666;
}
.left-panel {
display: none;
}
.left-panel.active {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fafafa;
}
.left-panel.active .sidebar {
background: #fff;
}
.left-panel__in {
width: 100%;
height: 100%;
max-width: 500px;
margin: 0 auto;
padding: 12px 24px;
}
.left-panel__contents {
width: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
}
.left-panel .shroom {
margin-bottom: 16px;
}
@media (min-width: 700px) {
body {
max-width: 1200px;
padding: 8px 16px;
padding-right: 274px;
}
.shroom {
display: none;
}
.page {
font-size: 18px;
}
.left-panel {
display: block;
position: fixed;
top: 0;
bottom: 0;
width: 274px;
right: 0;
}
.left-panel__contents {
height: 100%;
}
}
.sidebar {
padding: 16px 0;
border-radius: 1rem;
background: #f4f4f4;
}
.hypha-actions ul {
margin: 0;
padding: 0;
}
.hypha-actions li {
list-style: none;
}
.hypha-actions a {
display: block;
padding: 6px 16px;
font: inherit;
text-decoration: none;
color: #666;
transition: 0.1s background;
}
aside .hypha-actions a:hover {
background: #eaeaea;
}

View File

@ -0,0 +1,19 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": [
""
],
"name": "main.css",
"comment": "Update sys/main.css",
"author": "",
"time": 1592666188,
"text_mime": "text/css",
"binary_mime": "",
"text_name": "1.css",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,7 @@
var menu = document.getElementById('shroomburgerMenu');
document.getElementById('shroomBtn').addEventListener('click', function() {
menu.classList.add('active');
});
document.getElementById('mushroomBtn').addEventListener('click', function() {
menu.classList.remove('active');
});

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "main.js",
"comment": "Update sys/main.js",
"author": "",
"time": 1592937088,
"text_mime": "text/javascript",
"binary_mime": "",
"text_name": "1.mjs",
"binary_name": ""
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<head>
<title>Saved {{ .Name }}</title>
</head>
<body>
<p>Saved successfully. <a href="/{{ .Name }}">Go back</a></p>
</body>
</html>

View File

@ -0,0 +1,17 @@
{
"views": 0,
"deleted": false,
"revisions": {
"1": {
"tags": null,
"name": "updateOk.html",
"comment": "Create Templates/default-light/updateOk.html",
"author": "",
"time": 1592996644,
"text_mime": "text/html",
"binary_mime": "",
"text_name": "1.html",
"binary_name": ""
}
}
}