mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-12 05:20:26 +00:00
Merge remote-tracking branch 'origin/master' into features/templates-from-html
This commit is contained in:
commit
5ccaacb6d9
@ -3,9 +3,10 @@ package main
|
||||
const (
|
||||
TitleTemplate = `%s at MycorrhizaWiki`
|
||||
DefaultTitle = "MycorrhizaWiki"
|
||||
DefaultHeaderText = `MycorrhizaWiki`
|
||||
DefaultFooterText = "MycorrhizaWiki"
|
||||
DefaultHeaderText = `MycorrhizaWiki 🍄`
|
||||
DefaultFooterText = `This website runs <a href="https://github.com/bouncepaw/mycorrhiza">MycorrhizaWiki</a>.`
|
||||
DefaultSidebar = ""
|
||||
DefaultBodyBottom = ""
|
||||
DefaultContent = "It is empty here"
|
||||
DefaultStyles = `
|
||||
<link rel="stylesheet" href="/sys/main.css?action=raw">
|
||||
|
122
handlers.go
122
handlers.go
@ -1,8 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
@ -63,7 +68,118 @@ func HandlerRename(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
}
|
||||
|
||||
func HandlerUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
func makeTagsSlice(responseTagsString string) (ret []string) {
|
||||
// `responseTagsString` is string like "foo,, bar,kek". Whitespace around commas is insignificant. Expected output: []string{"foo", "bar", "kek"}
|
||||
for _, tag := range strings.Split(responseTagsString, ",") {
|
||||
if trimmed := strings.TrimSpace(tag); "" == trimmed {
|
||||
ret = append(ret, trimmed)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Return an existing hypha it exists in `hyphae` or create a new one. If it `isNew`, you'll have to insert it to `hyphae` yourself.
|
||||
func getHypha(name string) (*Hypha, bool) {
|
||||
log.Println("Accessing hypha", name)
|
||||
if h, ok := hyphae[name]; ok {
|
||||
log.Println("Got hypha", name)
|
||||
return h, false
|
||||
}
|
||||
log.Println("Create hypha", name)
|
||||
h := &Hypha{
|
||||
FullName: name,
|
||||
Path: filepath.Join(rootWikiDir, name),
|
||||
Revisions: make(map[string]*Revision),
|
||||
parentName: filepath.Dir(name),
|
||||
}
|
||||
return h, true
|
||||
}
|
||||
|
||||
// Create a new revison for hypha `h`. All data is fetched from `r`, except for BinaryMime and BinaryPath which require additional processing. You'll have te insert the revision to `h` yourself.
|
||||
func revisionFromHttpData(h *Hypha, r *http.Request) *Revision {
|
||||
idStr := strconv.Itoa(h.NewestRevisionInt() + 1)
|
||||
log.Println(idStr)
|
||||
rev := &Revision{
|
||||
Id: h.NewestRevisionInt() + 1,
|
||||
FullName: h.FullName,
|
||||
Tags: makeTagsSlice(r.PostFormValue("tags")),
|
||||
Comment: r.PostFormValue("comment"),
|
||||
Author: r.PostFormValue("author"),
|
||||
Time: int(time.Now().Unix()),
|
||||
TextMime: r.PostFormValue("text_mime"),
|
||||
TextPath: filepath.Join(h.Path, idStr+".txt"),
|
||||
// Left: BinaryMime, BinaryPath
|
||||
}
|
||||
return rev
|
||||
}
|
||||
|
||||
func writeTextFileFromHttpData(rev *Revision, r *http.Request) error {
|
||||
data := []byte(r.PostFormValue("text"))
|
||||
err := ioutil.WriteFile(rev.TextPath, data, 0644)
|
||||
if err != nil {
|
||||
log.Println("Failed to write", len(data), "bytes to", rev.TextPath)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func writeBinaryFileFromHttpData(h *Hypha, oldRev Revision, newRev *Revision, r *http.Request) error {
|
||||
// 10 MB file size limit
|
||||
r.ParseMultipartForm(10 << 20)
|
||||
// Read file
|
||||
file, handler, err := r.FormFile("binary")
|
||||
if file != nil {
|
||||
defer file.Close()
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("No binary data passed for", newRev.FullName)
|
||||
newRev.BinaryMime = oldRev.BinaryMime
|
||||
newRev.BinaryPath = oldRev.BinaryPath
|
||||
log.Println("Set previous revision's binary data")
|
||||
return nil
|
||||
}
|
||||
newRev.BinaryMime = handler.Header.Get("Content-Type")
|
||||
newRev.BinaryPath = filepath.Join(h.Path, newRev.IdAsStr()+".bin")
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
log.Println("Got", len(data), "of binary data for", newRev.FullName)
|
||||
err = ioutil.WriteFile(newRev.BinaryPath, data, 0644)
|
||||
if err != nil {
|
||||
log.Println("Failed to write", len(data), "bytes to", newRev.TextPath)
|
||||
return err
|
||||
}
|
||||
log.Println("Written", len(data), "of binary data for", newRev.FullName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandlerUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
log.Println("Attempt to update hypha", mux.Vars(r)["hypha"])
|
||||
h, isNew := getHypha(vars["hypha"])
|
||||
oldRev := h.GetNewestRevision()
|
||||
newRev := revisionFromHttpData(h, r)
|
||||
|
||||
if isNew {
|
||||
h.CreateDir()
|
||||
}
|
||||
err := writeTextFileFromHttpData(newRev, r)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = writeBinaryFileFromHttpData(h, oldRev, newRev, r)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
h.Revisions[newRev.IdAsStr()] = newRev
|
||||
h.SaveJson()
|
||||
|
||||
log.Println("Current hyphae storage is", hyphae)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("Saved successfully"))
|
||||
}
|
||||
|
47
hypha.go
47
hypha.go
@ -1,20 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Hypha struct {
|
||||
FullName string
|
||||
Path string
|
||||
FullName string `json:"-"`
|
||||
Path string `json:"-"`
|
||||
ViewCount int `json:"views"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Revisions map[string]*Revision `json:"revisions"`
|
||||
ChildrenNames []string
|
||||
ChildrenNames []string `json:"-"`
|
||||
parentName string
|
||||
}
|
||||
|
||||
@ -39,7 +43,15 @@ func (h *Hypha) Name() string {
|
||||
return h.FullName
|
||||
}
|
||||
|
||||
func (h *Hypha) GetNewestRevision() Revision {
|
||||
return *h.Revisions[h.NewestRevision()]
|
||||
}
|
||||
|
||||
func (h *Hypha) NewestRevision() string {
|
||||
return strconv.Itoa(h.NewestRevisionInt())
|
||||
}
|
||||
|
||||
func (h *Hypha) NewestRevisionInt() int {
|
||||
var largest int
|
||||
for k, _ := range h.Revisions {
|
||||
rev, _ := strconv.Atoi(k)
|
||||
@ -47,16 +59,38 @@ func (h *Hypha) NewestRevision() string {
|
||||
largest = rev
|
||||
}
|
||||
}
|
||||
return strconv.Itoa(largest)
|
||||
return largest
|
||||
}
|
||||
|
||||
func (h *Hypha) MetaJsonPath() string {
|
||||
return filepath.Join(h.Path, "meta.json")
|
||||
}
|
||||
|
||||
func (h *Hypha) CreateDir() error {
|
||||
return os.MkdirAll(h.Path, 0644)
|
||||
}
|
||||
|
||||
func (h *Hypha) ParentName() string {
|
||||
return h.parentName
|
||||
}
|
||||
|
||||
func (h *Hypha) SaveJson() {
|
||||
data, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
log.Println("Failed to create JSON of hypha.", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(h.MetaJsonPath(), data, 0644)
|
||||
if err != nil {
|
||||
log.Println("Failed to save JSON of hypha.", err)
|
||||
return
|
||||
}
|
||||
log.Println("Saved JSON data of", h.FullName)
|
||||
}
|
||||
|
||||
func ActionEdit(hyphaName string, w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
var initContents, initTextMime, initBinaryMime, initTags string
|
||||
var initContents, initTextMime, initTags string
|
||||
hypha, ok := hyphae[hyphaName]
|
||||
if !ok {
|
||||
initContents = "Describe " + hyphaName + "here."
|
||||
@ -71,10 +105,9 @@ func ActionEdit(hyphaName string, w http.ResponseWriter) {
|
||||
}
|
||||
initContents = string(contents)
|
||||
initTextMime = newestRev.TextMime
|
||||
initBinaryMime = newestRev.BinaryMime
|
||||
initTags = strings.Join(newestRev.Tags, ",")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(EditHyphaPage(hyphaName, initTextMime, initBinaryMime, initContents, initTags)))
|
||||
w.Write([]byte(EditHyphaPage(hyphaName, initTextMime, initContents, initTags)))
|
||||
}
|
||||
|
4
main.go
4
main.go
@ -101,7 +101,9 @@ func main() {
|
||||
r.Queries("action", "rename", "to", hyphaPattern).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerRename)
|
||||
|
||||
r.Queries("action", "update").Path(hyphaUrl).
|
||||
r.Queries(
|
||||
"action", "update",
|
||||
).Path(hyphaUrl).Methods("POST").
|
||||
HandlerFunc(HandlerUpdate)
|
||||
|
||||
r.HandleFunc(hyphaUrl, HandlerView)
|
||||
|
25
render.go
25
render.go
@ -3,29 +3,36 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func EditHyphaPage(name, text_mime, binary_mime, content, tags string) string {
|
||||
func EditHyphaPage(name, textMime, content, tags string) string {
|
||||
keys := map[string]string{
|
||||
"Title": fmt.Sprintf(TitleTemplate, name),
|
||||
"Title": fmt.Sprintf(TitleTemplate, "Edit "+name),
|
||||
"Header": renderFromString(name, "Hypha/edit/header.html"),
|
||||
}
|
||||
page := map[string]string{
|
||||
"Text": content,
|
||||
"TextMime": text_mime,
|
||||
"BinMime": binary_mime,
|
||||
"TextMime": textMime,
|
||||
"Name": name,
|
||||
"Tags": tags,
|
||||
}
|
||||
return renderBase(renderFromMap(page, "Hypha/edit.html"), keys)
|
||||
return renderBase(renderFromMap(page, "Hypha/edit/index.html"), keys)
|
||||
}
|
||||
|
||||
func HyphaPage(hyphae map[string]*Hypha, rev Revision, content string) string {
|
||||
sidebar := DefaultSidebar
|
||||
bside, err := ioutil.ReadFile("Hypha/view/sidebar.html")
|
||||
if err == nil {
|
||||
sidebar = string(bside)
|
||||
}
|
||||
keys := map[string]string{
|
||||
"Title": fmt.Sprintf(TitleTemplate, rev.FullName),
|
||||
"Sidebar": sidebar,
|
||||
}
|
||||
return renderBase(renderFromString(content, "Hypha/index.html"), keys)
|
||||
return renderBase(renderFromString(content, "Hypha/view/index.html"), keys)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -37,10 +44,12 @@ Args:
|
||||
func renderBase(content string, keys map[string]string) string {
|
||||
page := map[string]string{
|
||||
"Title": DefaultTitle,
|
||||
"Header": renderFromString(DefaultHeaderText, "header.html"),
|
||||
"Footer": renderFromString(DefaultFooterText, "footer.html"),
|
||||
"Head": DefaultStyles,
|
||||
"Sidebar": DefaultSidebar,
|
||||
"Main": DefaultContent,
|
||||
"BodyBottom": DefaultBodyBottom,
|
||||
"Header": renderFromString(DefaultHeaderText, "header.html"),
|
||||
"Footer": renderFromString(DefaultFooterText, "footer.html"),
|
||||
}
|
||||
for key, val := range keys {
|
||||
page[key] = val
|
||||
|
15
revision.go
15
revision.go
@ -6,12 +6,15 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/gomarkdown/markdown"
|
||||
)
|
||||
|
||||
// In different places, revision variable is called `r`. But when there is an http.Request as well, the revision becomes `rev`. TODO: name them consistently.
|
||||
type Revision struct {
|
||||
Id int
|
||||
FullName string
|
||||
Id int `json:"-"`
|
||||
FullName string `json:"-"`
|
||||
Tags []string `json:"tags"`
|
||||
ShortName string `json:"name"`
|
||||
Comment string `json:"comment"`
|
||||
@ -19,8 +22,12 @@ type Revision struct {
|
||||
Time int `json:"time"`
|
||||
TextMime string `json:"text_mime"`
|
||||
BinaryMime string `json:"binary_mime"`
|
||||
TextPath string
|
||||
BinaryPath string
|
||||
TextPath string `json:"-"`
|
||||
BinaryPath string `json:"-"`
|
||||
}
|
||||
|
||||
func (r *Revision) IdAsStr() string {
|
||||
return strconv.Itoa(r.Id)
|
||||
}
|
||||
|
||||
// During initialisation, it is guaranteed that r.BinaryMime is set to "" if the revision has no binary data.
|
||||
|
1
templates/Hypha/edit/header.html
Normal file
1
templates/Hypha/edit/header.html
Normal file
@ -0,0 +1 @@
|
||||
<h1 class="header__edit-title">Edit {{ . }}</h1>
|
@ -1,7 +1,10 @@
|
||||
<div class="naviwrapper">
|
||||
<form class="naviwrapper__edit edit-box">
|
||||
<form class="naviwrapper__edit edit-box"
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
action="?action=update">
|
||||
<div class="naviwrapper__buttons">
|
||||
<input type="submit" name="action" value="update"/>
|
||||
<input type="submit" value="update"/>
|
||||
</div>
|
||||
|
||||
<div class="edit-box__left">
|
||||
@ -20,17 +23,13 @@
|
||||
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
|
||||
<input type="text" name="text_mime" value="{{ .TextMime }}"/>
|
||||
|
||||
<h4>Media MIME-type</h4>
|
||||
<p>For now, only image formats are supported. Choose any, but <code>image/jpeg</code> and <code>image/png</code> are recommended</p>
|
||||
<input type="text" name="binary_mime" value="{{ .BinMime }}"/>
|
||||
|
||||
<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="comment" value="{{ .Tags }}"/>
|
||||
<input type="text" name="tags" value="{{ .Tags }}"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
9
templates/Hypha/view/bodybottom.html
Normal file
9
templates/Hypha/view/bodybottom.html
Normal file
@ -0,0 +1,9 @@
|
||||
<script type="text/javascript">
|
||||
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');
|
||||
});
|
||||
</script>
|
10
templates/Hypha/view/sidebar.html
Normal file
10
templates/Hypha/view/sidebar.html
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="naviwrapper">
|
||||
<div class="hypha-actions">
|
||||
<ul>
|
||||
<li><a href="?action=edit">Edit</a>
|
||||
<li><a href="?action=getBinary">Download</a>
|
||||
<li><a href="?action=zen">Zen mode</a>
|
||||
<li><a href="?action=raw">View raw</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -1,11 +1,25 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title }}</title>
|
||||
{{ .Head }}
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">{{ .Header }}</header>
|
||||
<div class="shroom">
|
||||
<button class="shroom__button" id="shroomBtn"><span>🍄</span> Open mycelium</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 mycelium</button>
|
||||
</div>
|
||||
<div class="left-panel__contents">
|
||||
<header class="header">{{ .Header }}</header>
|
||||
<aside class="sidebar">{{ .Sidebar }}</aside>
|
||||
<footer class="footer">{{ .Footer }}</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ .BodyBottom }}
|
||||
</body>
|
||||
</html>
|
BIN
w/m/Fruit/Apple/2.bin
Normal file
BIN
w/m/Fruit/Apple/2.bin
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
3
w/m/Fruit/Apple/2.txt
Normal file
3
w/m/Fruit/Apple/2.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Красное яблоко. Для съёмки использовалась белая бумага позади и над яблоком, и фотовспышка SB-600 в 1/4 мощности.
|
||||
|
||||
Source: https://commons.wikimedia.org/wiki/File:Red_Apple.jpg
|
6
w/m/Fruit/Apple/3.txt
Normal file
6
w/m/Fruit/Apple/3.txt
Normal file
@ -0,0 +1,6 @@
|
||||
Mycorrhiza is pure happiness
|
||||
|
||||
Красное яблоко. Для съёмки использовалась белая бумага позади и над яблоком, и фотовспышка SB-600 в 1/4 мощности.
|
||||
|
||||
Source: https://commons.wikimedia.org/wiki/File:Red_Apple.jpg
|
||||
|
BIN
w/m/Fruit/Apple/4.bin
Normal file
BIN
w/m/Fruit/Apple/4.bin
Normal file
Binary file not shown.
5
w/m/Fruit/Apple/4.txt
Normal file
5
w/m/Fruit/Apple/4.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Mycorrhiza is pure happiness
|
||||
|
||||
Красное яблоко. Для съёмки использовалась белая бумага позади и над яблоком, и фотовспышка SB-600 в 1/4 мощности.
|
||||
|
||||
Source: https://commons.wikimedia.org/wiki/File:Red_Apple.jpg
|
@ -1,13 +1 @@
|
||||
{
|
||||
"revisions":{
|
||||
"1":{
|
||||
"name": "Apple",
|
||||
"time": 1591639464,
|
||||
"author": "bouncepaw",
|
||||
"comment": "add apple pic hehehe",
|
||||
"tags": ["img"],
|
||||
"text_mime": "text/plain",
|
||||
"binary_mime": "image/jpeg"
|
||||
}
|
||||
}
|
||||
}
|
||||
{"views":0,"deleted":false,"revisions":{"1":{"tags":["img"],"name":"Apple","comment":"add apple pic hehehe","author":"bouncepaw","time":1591639464,"text_mime":"text/plain","binary_mime":"image/jpeg"},"2":{"tags":null,"name":"","comment":"Update Fruit/Apple","author":"","time":1592570366,"text_mime":"text/plain","binary_mime":"image/jpeg"},"3":{"tags":null,"name":"","comment":"Test fs dumping","author":"","time":1592570926,"text_mime":"text/plain","binary_mime":"image/jpeg"},"4":{"tags":[""],"name":"","comment":"Update Fruit/Apple","author":"","time":1592579243,"text_mime":"text/plain","binary_mime":"application/pdf"}}}
|
@ -1,7 +1,236 @@
|
||||
b { color: red; }
|
||||
article { border: 1px black solid; }
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 15px/1.5 system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', 'Roboto', 'Arial', sans-serif;
|
||||
max-width: 500px;
|
||||
min-height: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
body {
|
||||
|
||||
/*
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 250px;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-column-gap: 24px;
|
||||
grid-template-areas:
|
||||
"main header"
|
||||
"main sidebar"
|
||||
"main footer";
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
.shroom {
|
||||
/*margin: 8px 0 24px;*/
|
||||
/*margin: -12px;*/
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shroom__button {
|
||||
/*
|
||||
padding: 2px 12px;
|
||||
border: 1px solid #ddd;
|
||||
*/border-radius: 8px;/*
|
||||
background: none;
|
||||
*/
|
||||
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 {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
main {
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
aside {
|
||||
grid-area: sidebar;
|
||||
}
|
||||
|
||||
footer {
|
||||
grid-area: footer;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
.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: 1em 0 0.25em;
|
||||
}
|
||||
|
||||
.page {
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.666;
|
||||
max-width: 40em;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.page__title {
|
||||
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 }
|
||||
|
||||
footer {
|
||||
/*margin-top: 40px;
|
||||
border-top: 1px solid #ddd;*/
|
||||
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; /* 250px + 16px + 16px */
|
||||
}
|
||||
|
||||
.shroom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 274px; /* 250px + 24px * 2 */
|
||||
/*right: calc(50% + 900px / 2 + 24px);*/
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.left-panel__contents {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 16px 0;
|
||||
border-radius: 8px;
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user