mirror of
				https://github.com/osmarks/mycorrhiza.git
				synced 2025-10-31 15:43:00 +00:00 
			
		
		
		
	Make mycelia somewhat working
This commit is contained in:
		| @@ -18,8 +18,8 @@ const ( | |||||||
| 	HyphaUrl        = `/{hypha:` + HyphaPattern + `}` | 	HyphaUrl        = `/{hypha:` + HyphaPattern + `}` | ||||||
| 	RevisionPattern = `[\d]+` | 	RevisionPattern = `[\d]+` | ||||||
| 	RevQuery        = `{rev:` + RevisionPattern + `}` | 	RevQuery        = `{rev:` + RevisionPattern + `}` | ||||||
| 	MyceliumPattern = `:` + HyphaPattern | 	MyceliumPattern = `[^\s\d:/?&\\][^:?&\\/]*` | ||||||
| 	MyceliumUrl     = `/{mycelium:` + MyceliumPattern + `}` | 	MyceliumUrl     = `/:{mycelium:` + MyceliumPattern + `}` | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|   | |||||||
| @@ -92,5 +92,5 @@ func navitreeEntry(name, class string) string { | |||||||
| 	return fmt.Sprintf(`<li class="navitree__entry %s"> | 	return fmt.Sprintf(`<li class="navitree__entry %s"> | ||||||
| 	<a class="navitree__link" href="/%s">%s</a> | 	<a class="navitree__link" href="/%s">%s</a> | ||||||
| </li> | </li> | ||||||
| `, class, util.DisplayToCanonical(name), filepath.Base(name)) | `, class, ":"+util.DisplayToCanonical(name), filepath.Base(name)) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ func (h *Hypha) asHtml() (string, error) { | |||||||
| ` | ` | ||||||
| 	// What about using <figure>? | 	// What about using <figure>? | ||||||
| 	if h.hasBinaryData() { | 	if h.hasBinaryData() { | ||||||
| 		ret += fmt.Sprintf(`<img src="/%s?action=binary&rev=%d" class="page__amnt"/>`, util.DisplayToCanonical(rev.FullName), rev.Id) | 		ret += fmt.Sprintf(`<img src="/:%s?action=binary&rev=%d" class="page__amnt"/>`, util.DisplayToCanonical(rev.FullName), rev.Id) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	contents, err := ioutil.ReadFile(rev.TextPath) | 	contents, err := ioutil.ReadFile(rev.TextPath) | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								fs/hypha.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								fs/hypha.go
									
									
									
									
									
								
							| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/bouncepaw/mycorrhiza/mycelium" | ||||||
| 	"github.com/bouncepaw/mycorrhiza/util" | 	"github.com/bouncepaw/mycorrhiza/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -32,7 +33,16 @@ func (h *Hypha) Invalidate(err error) *Hypha { | |||||||
| 	return h | 	return h | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Storage) Open(name string) *Hypha { | func (s *Storage) OpenFromMap(m map[string]string) *Hypha { | ||||||
|  | 	name := mycelium.NameWithMyceliumInMap(m) | ||||||
|  | 	h := s.open(name) | ||||||
|  | 	if rev, ok := m["rev"]; ok { | ||||||
|  | 		return h.OnRevision(rev) | ||||||
|  | 	} | ||||||
|  | 	return h | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Storage) open(name string) *Hypha { | ||||||
| 	name = util.UrlToCanonical(name) | 	name = util.UrlToCanonical(name) | ||||||
| 	h := &Hypha{ | 	h := &Hypha{ | ||||||
| 		Exists:   true, | 		Exists:   true, | ||||||
| @@ -110,6 +120,7 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha { | |||||||
| 	if h.Invalid { | 	if h.Invalid { | ||||||
| 		return h | 		return h | ||||||
| 	} | 	} | ||||||
|  | 	if h.Exists { | ||||||
| 		fileContents, err := ioutil.ReadFile(h.actual.TextPath) | 		fileContents, err := ioutil.ReadFile(h.actual.TextPath) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return h.Invalidate(err) | 			return h.Invalidate(err) | ||||||
| @@ -117,6 +128,10 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha { | |||||||
| 		w.Header().Set("Content-Type", h.mimeTypeForActionRaw()) | 		w.Header().Set("Content-Type", h.mimeTypeForActionRaw()) | ||||||
| 		w.WriteHeader(http.StatusOK) | 		w.WriteHeader(http.StatusOK) | ||||||
| 		w.Write(fileContents) | 		w.Write(fileContents) | ||||||
|  | 	} else { | ||||||
|  | 		log.Println("Hypha", h.FullName, "has no actual revision") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	} | ||||||
| 	return h | 	return h | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -126,6 +141,7 @@ func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha { | |||||||
| 	if h.Invalid { | 	if h.Invalid { | ||||||
| 		return h | 		return h | ||||||
| 	} | 	} | ||||||
|  | 	if h.Exists { | ||||||
| 		fileContents, err := ioutil.ReadFile(h.actual.BinaryPath) | 		fileContents, err := ioutil.ReadFile(h.actual.BinaryPath) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return h.Invalidate(err) | 			return h.Invalidate(err) | ||||||
| @@ -133,6 +149,10 @@ func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha { | |||||||
| 		w.Header().Set("Content-Type", h.actual.BinaryMime) | 		w.Header().Set("Content-Type", h.actual.BinaryMime) | ||||||
| 		w.WriteHeader(http.StatusOK) | 		w.WriteHeader(http.StatusOK) | ||||||
| 		w.Write(fileContents) | 		w.Write(fileContents) | ||||||
|  | 	} else { | ||||||
|  | 		log.Println("Hypha", h.FullName, "has no actual revision") | ||||||
|  | 		w.WriteHeader(http.StatusNotFound) | ||||||
|  | 	} | ||||||
| 	return h | 	return h | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,56 +0,0 @@ | |||||||
| package fs |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" |  | ||||||
|  |  | ||||||
| 	"github.com/bouncepaw/mycorrhiza/cfg" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	MainMycelium   string |  | ||||||
| 	SystemMycelium string |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func gatherDirNames(path string) map[string]struct{} { |  | ||||||
| 	res := make(map[string]struct{}) |  | ||||||
| 	nodes, err := ioutil.ReadDir(path) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	for _, node := range nodes { |  | ||||||
| 		if node.IsDir() { |  | ||||||
| 			res[node.Name()] = struct{}{} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return res |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func VerifyMycelia() { |  | ||||||
| 	var ( |  | ||||||
| 		dirs          = gatherDirNames(cfg.WikiDir) |  | ||||||
| 		mainPresent   bool |  | ||||||
| 		systemPresent bool |  | ||||||
| 	) |  | ||||||
| 	for _, mycelium := range cfg.Mycelia { |  | ||||||
| 		switch mycelium.Type { |  | ||||||
| 		case "main": |  | ||||||
| 			mainPresent = true |  | ||||||
| 			MainMycelium = mycelium.Names[0] |  | ||||||
| 		case "system": |  | ||||||
| 			systemPresent = true |  | ||||||
| 			SystemMycelium = mycelium.Names[0] |  | ||||||
| 		} |  | ||||||
| 		// Check if there is a dir corresponding to the mycelium |  | ||||||
| 		if _, ok := dirs[mycelium.Names[0]]; !ok { |  | ||||||
| 			log.Fatal("No directory found for mycelium " + mycelium.Names[0]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !mainPresent { |  | ||||||
| 		log.Fatal("No `main` mycelium given in config.json") |  | ||||||
| 	} |  | ||||||
| 	if !systemPresent { |  | ||||||
| 		log.Fatal("No `system` mycelium given in config.json") |  | ||||||
| 	} |  | ||||||
| 	log.Println("Mycelial dirs are present") |  | ||||||
| } |  | ||||||
| @@ -14,7 +14,7 @@ import ( | |||||||
| // Boilerplate code present in many handlers. Good to have it. | // Boilerplate code present in many handlers. Good to have it. | ||||||
| func HandlerBase(w http.ResponseWriter, rq *http.Request) *fs.Hypha { | func HandlerBase(w http.ResponseWriter, rq *http.Request) *fs.Hypha { | ||||||
| 	vars := mux.Vars(rq) | 	vars := mux.Vars(rq) | ||||||
| 	return fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars)) | 	return fs.Hs.OpenFromMap(vars).OnRevision(RevInMap(vars)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func HandlerRaw(w http.ResponseWriter, rq *http.Request) { | func HandlerRaw(w http.ResponseWriter, rq *http.Request) { | ||||||
| @@ -41,7 +41,7 @@ func HandlerView(w http.ResponseWriter, rq *http.Request) { | |||||||
|  |  | ||||||
| func HandlerEdit(w http.ResponseWriter, rq *http.Request) { | func HandlerEdit(w http.ResponseWriter, rq *http.Request) { | ||||||
| 	vars := mux.Vars(rq) | 	vars := mux.Vars(rq) | ||||||
| 	h := fs.Hs.Open(vars["hypha"]).OnRevision("0") | 	h := fs.Hs.OpenFromMap(vars).OnRevision("0") | ||||||
| 	w.Header().Set("Content-Type", "text/html;charset=utf-8") | 	w.Header().Set("Content-Type", "text/html;charset=utf-8") | ||||||
| 	w.WriteHeader(http.StatusOK) | 	w.WriteHeader(http.StatusOK) | ||||||
| 	w.Write(render.HyphaEdit(h)) | 	w.Write(render.HyphaEdit(h)) | ||||||
| @@ -51,7 +51,7 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) { | |||||||
| 	vars := mux.Vars(rq) | 	vars := mux.Vars(rq) | ||||||
| 	log.Println("Attempt to update hypha", vars["hypha"]) | 	log.Println("Attempt to update hypha", vars["hypha"]) | ||||||
| 	h := fs.Hs. | 	h := fs.Hs. | ||||||
| 		Open(vars["hypha"]). | 		OpenFromMap(vars). | ||||||
| 		CreateDirIfNeeded(). | 		CreateDirIfNeeded(). | ||||||
| 		AddRevisionFromHttpData(rq). | 		AddRevisionFromHttpData(rq). | ||||||
| 		WriteTextFileFromHttpData(rq). | 		WriteTextFileFromHttpData(rq). | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/bouncepaw/mycorrhiza/cfg" | 	"github.com/bouncepaw/mycorrhiza/cfg" | ||||||
| 	"github.com/bouncepaw/mycorrhiza/fs" | 	"github.com/bouncepaw/mycorrhiza/fs" | ||||||
|  | 	"github.com/bouncepaw/mycorrhiza/mycelium" | ||||||
| 	"github.com/gorilla/mux" | 	"github.com/gorilla/mux" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -53,7 +54,7 @@ func main() { | |||||||
| 	log.Println("Welcome to MycorrhizaWiki α") | 	log.Println("Welcome to MycorrhizaWiki α") | ||||||
| 	cfg.InitConfig(wikiDir) | 	cfg.InitConfig(wikiDir) | ||||||
| 	log.Println("Indexing hyphae...") | 	log.Println("Indexing hyphae...") | ||||||
| 	fs.VerifyMycelia() | 	mycelium.Init() | ||||||
| 	fs.InitStorage() | 	fs.InitStorage() | ||||||
|  |  | ||||||
| 	// Start server code. See handlers.go for handlers' implementations. | 	// Start server code. See handlers.go for handlers' implementations. | ||||||
| @@ -78,6 +79,7 @@ func main() { | |||||||
| 	r.Queries("action", "update").Path(cfg.HyphaUrl). | 	r.Queries("action", "update").Path(cfg.HyphaUrl). | ||||||
| 		Methods("POST").HandlerFunc(HandlerUpdate) | 		Methods("POST").HandlerFunc(HandlerUpdate) | ||||||
|  |  | ||||||
|  | 	r.HandleFunc(cfg.MyceliumUrl+cfg.HyphaUrl, HandlerView) | ||||||
| 	r.HandleFunc(cfg.HyphaUrl, HandlerView) | 	r.HandleFunc(cfg.HyphaUrl, HandlerView) | ||||||
|  |  | ||||||
| 	// Debug page that renders all hyphae. | 	// Debug page that renders all hyphae. | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								mycelium/mycelium.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								mycelium/mycelium.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | package mycelium | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/bouncepaw/mycorrhiza/cfg" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	MainMycelium   string | ||||||
|  | 	SystemMycelium string | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func gatherDirNames(path string) map[string]struct{} { | ||||||
|  | 	res := make(map[string]struct{}) | ||||||
|  | 	nodes, err := ioutil.ReadDir(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	for _, node := range nodes { | ||||||
|  | 		if node.IsDir() { | ||||||
|  | 			res[node.Name()] = struct{}{} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add values to the set. If a value is already there, return false. | ||||||
|  | func addInUniqueSet(set map[string]struct{}, names []string) bool { | ||||||
|  | 	ok := true | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		if _, present := set[name]; present { | ||||||
|  | 			ok = false | ||||||
|  | 		} | ||||||
|  | 		set[name] = struct{}{} | ||||||
|  | 	} | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Init() { | ||||||
|  | 	var ( | ||||||
|  | 		// Used to check if there are no duplicates | ||||||
|  | 		foundNames    = make(map[string]struct{}) | ||||||
|  | 		dirs          = gatherDirNames(cfg.WikiDir) | ||||||
|  | 		mainPresent   bool | ||||||
|  | 		systemPresent bool | ||||||
|  | 	) | ||||||
|  | 	for _, mycelium := range cfg.Mycelia { | ||||||
|  | 		switch mycelium.Type { | ||||||
|  | 		case "main": | ||||||
|  | 			mainPresent = true | ||||||
|  | 			MainMycelium = mycelium.Names[0] | ||||||
|  | 		case "system": | ||||||
|  | 			systemPresent = true | ||||||
|  | 			SystemMycelium = mycelium.Names[0] | ||||||
|  | 		} | ||||||
|  | 		// Check if there is a dir corresponding to the mycelium | ||||||
|  | 		if _, ok := dirs[mycelium.Names[0]]; !ok { | ||||||
|  | 			log.Fatal("No directory found for mycelium " + mycelium.Names[0]) | ||||||
|  | 		} | ||||||
|  | 		// Confirm uniqueness of names | ||||||
|  | 		if ok := addInUniqueSet(foundNames, mycelium.Names); !ok { | ||||||
|  | 			log.Fatal("At least one name was used more than once for mycelia") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !mainPresent { | ||||||
|  | 		log.Fatal("No `main` mycelium given in config.json") | ||||||
|  | 	} | ||||||
|  | 	if !systemPresent { | ||||||
|  | 		log.Fatal("No `system` mycelium given in config.json") | ||||||
|  | 	} | ||||||
|  | 	log.Println("Mycelial dirs are present") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NameWithMyceliumInMap(m map[string]string) (res string) { | ||||||
|  | 	var ( | ||||||
|  | 		hyphaName, okH = m["hypha"] | ||||||
|  | 		mycelName, okM = m["mycelium"] | ||||||
|  | 	) | ||||||
|  | 	log.Println(m) | ||||||
|  | 	if !okH { | ||||||
|  | 		// It will result in an error when trying to open a hypha with such name | ||||||
|  | 		return ":::" | ||||||
|  | 	} | ||||||
|  | 	if okM { | ||||||
|  | 		res = canonicalMycelium(mycelName) | ||||||
|  | 	} else { | ||||||
|  | 		res = MainMycelium | ||||||
|  | 	} | ||||||
|  | 	return res + "/" + hyphaName | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func canonicalMycelium(name string) string { | ||||||
|  | 	log.Println("Determining canonical mycelial name for", name) | ||||||
|  | 	name = strings.ToLower(name) | ||||||
|  | 	for _, mycel := range cfg.Mycelia { | ||||||
|  | 		for _, mycelName := range mycel.Names { | ||||||
|  | 			if mycelName == name { | ||||||
|  | 				return mycel.Names[0] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// This is a nonexistent mycelium. Return a name that will trigger an error | ||||||
|  | 	return ":error:" | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/bouncepaw/mycorrhiza/cfg" | 	"github.com/bouncepaw/mycorrhiza/cfg" | ||||||
| 	"github.com/bouncepaw/mycorrhiza/fs" | 	"github.com/bouncepaw/mycorrhiza/fs" | ||||||
|  | 	"github.com/bouncepaw/mycorrhiza/mycelium" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // HyphaEdit renders hypha editor. | // HyphaEdit renders hypha editor. | ||||||
| @@ -85,7 +86,12 @@ type Layout struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func layout(name string) *Layout { | func layout(name string) *Layout { | ||||||
| 	h := fs.Hs.Open(path.Join(fs.SystemMycelium, "theme", cfg.Theme, name+".html")).OnRevision("0") | 	lytName := path.Join("theme", cfg.Theme, name+".html") | ||||||
|  | 	h := fs.Hs.OpenFromMap(map[string]string{ | ||||||
|  | 		"mycelium": mycelium.SystemMycelium, | ||||||
|  | 		"hypha":    lytName, | ||||||
|  | 		"rev":      "0", | ||||||
|  | 	}) | ||||||
| 	if h.Invalid { | 	if h.Invalid { | ||||||
| 		return &Layout{nil, nil, true, h.Err} | 		return &Layout{nil, nil, true, h.Err} | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								util/util.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								util/util.go
									
									
									
									
									
								
							| @@ -5,12 +5,28 @@ import ( | |||||||
| 	"unicode" | 	"unicode" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func addColonPerhaps(name string) string { | ||||||
|  | 	if strings.HasPrefix(name, ":") { | ||||||
|  | 		return name | ||||||
|  | 	} | ||||||
|  | 	return ":" + name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func removeColonPerhaps(name string) string { | ||||||
|  | 	if strings.HasPrefix(name, ":") { | ||||||
|  | 		return name[1:] | ||||||
|  | 	} | ||||||
|  | 	return name | ||||||
|  | } | ||||||
|  |  | ||||||
| func UrlToCanonical(name string) string { | func UrlToCanonical(name string) string { | ||||||
| 	return strings.ToLower(strings.ReplaceAll(name, " ", "_")) | 	return removeColonPerhaps( | ||||||
|  | 		strings.ToLower(strings.ReplaceAll(name, " ", "_"))) | ||||||
| } | } | ||||||
|  |  | ||||||
| func DisplayToCanonical(name string) string { | func DisplayToCanonical(name string) string { | ||||||
| 	return strings.ToLower(strings.ReplaceAll(name, " ", "_")) | 	return removeColonPerhaps( | ||||||
|  | 		strings.ToLower(strings.ReplaceAll(name, " ", "_"))) | ||||||
| } | } | ||||||
|  |  | ||||||
| func CanonicalToDisplay(name string) (res string) { | func CanonicalToDisplay(name string) (res string) { | ||||||
| @@ -29,5 +45,5 @@ func CanonicalToDisplay(name string) (res string) { | |||||||
| 		} | 		} | ||||||
| 		res += string(ch) | 		res += string(ch) | ||||||
| 	} | 	} | ||||||
| 	return res | 	return addColonPerhaps(res) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								wiki/sys/theme/2.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								wiki/sys/theme/2.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # Themes | ||||||
|  |  | ||||||
|  | In MycorrhizaWiki, themes are used to specify what is shown to a user, the front-end. So, a theme consists of: | ||||||
|  |  | ||||||
|  | - **HTML templates.** Data is inserted into them. | ||||||
|  | - **CSS.** | ||||||
|  | - **JS.** | ||||||
|  |  | ||||||
|  | Your HTML template structure must be identical to the one. You can include any CSS and JS in your themes. | ||||||
|  |  | ||||||
|  | See [default-light](:sys/theme/default-light) theme for more information. This is the only theme that is guaranteed to work. | ||||||
							
								
								
									
										11
									
								
								wiki/sys/theme/3.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								wiki/sys/theme/3.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # Themes | ||||||
|  |  | ||||||
|  | In MycorrhizaWiki, themes are used to specify what is shown to a user, the front-end. So, a theme consists of: | ||||||
|  |  | ||||||
|  | - **HTML templates.** Data is inserted into them. | ||||||
|  | - **CSS.** | ||||||
|  | - **JS.** | ||||||
|  |  | ||||||
|  | Your HTML template structure must be identical to the one. You can include any CSS and JS in your themes. | ||||||
|  |  | ||||||
|  | See [default-light](/:sys/theme/default-light) theme for more information. This is the only theme that is guaranteed to work. | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| <html> | <html> | ||||||
| 	<head> | 	<head> | ||||||
| 		<title>{{ .Title }}</title> | 		<title>{{ .Title }}</title> | ||||||
| 		<link rel="stylesheet" href="/sys/theme/default-light/main.css?action=raw"> | 		<link rel="stylesheet" href="/:sys/theme/default-light/main.css?action=raw"> | ||||||
| 	</head> | 	</head> | ||||||
| 	<body> | 	<body> | ||||||
| 		<header class="header"> | 		<header class="header"> | ||||||
| @@ -20,6 +20,6 @@ | |||||||
| 		<footer> | 		<footer> | ||||||
| 			<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p> | 			<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p> | ||||||
| 		</footer> | 		</footer> | ||||||
| 		<script src="/sys/theme/default-light/main.js?action=raw"></script> | 		<script src="/:sys/theme/default-light/main.js?action=raw"></script> | ||||||
| 	</body> | 	</body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -15,10 +15,10 @@ h1, h2, h3, h4, h5, h6 { margin: 0.5em 0 0.25em; } | |||||||
| code { background-color: #f4f4f4; } | code { background-color: #f4f4f4; } | ||||||
| .page { line-height: 1.666; max-width: 40rem; hyphens: auto; } | .page { line-height: 1.666; max-width: 40rem; hyphens: auto; } | ||||||
| .page img { max-width:100%; } | .page img { max-width:100%; } | ||||||
| .page pre { white-space: break-spaces; } | .page pre { white-space: break-spaces; background-color: #f4f4f4; } | ||||||
| .page__title { font-size: 2rem; margin: 0; } | .page__title { font-size: 2rem; margin: 0; } | ||||||
|  |  | ||||||
| footer { padding: 1rem 0; font-size: .8rem; bottom: 0; position: absolute; } | footer { padding: 1rem 0; font-size: .8rem; } | ||||||
| footer a, footer a:visited { color: black; } | footer a, footer a:visited { color: black; } | ||||||
| /* Sidebar section */ | /* Sidebar section */ | ||||||
| .sidebar { padding: 1rem 0; background: #f4f4f4; } | .sidebar { padding: 1rem 0; background: #f4f4f4; } | ||||||
|   | |||||||
| @@ -14,8 +14,32 @@ | |||||||
| 			"binary_mime": "", | 			"binary_mime": "", | ||||||
| 			"text_name": "1.markdown", | 			"text_name": "1.markdown", | ||||||
| 			"binary_name": "" | 			"binary_name": "" | ||||||
| 		} |  | ||||||
| 		}, | 		}, | ||||||
| 	"Invalid": false, | 		"2": { | ||||||
| 	"Err": null | 			"tags": [ | ||||||
|  | 				"" | ||||||
|  | 			], | ||||||
|  | 			"name": "Theme", | ||||||
|  | 			"comment": "Update Sys/Theme", | ||||||
|  | 			"author": "", | ||||||
|  | 			"time": 1593802668, | ||||||
|  | 			"text_mime": "text/markdown", | ||||||
|  | 			"binary_mime": "", | ||||||
|  | 			"text_name": "2.markdown", | ||||||
|  | 			"binary_name": "" | ||||||
|  | 		}, | ||||||
|  | 		"3": { | ||||||
|  | 			"tags": [ | ||||||
|  | 				"" | ||||||
|  | 			], | ||||||
|  | 			"name": "Theme", | ||||||
|  | 			"comment": "Update Sys/Theme", | ||||||
|  | 			"author": "", | ||||||
|  | 			"time": 1593802769, | ||||||
|  | 			"text_mime": "text/markdown", | ||||||
|  | 			"binary_mime": "", | ||||||
|  | 			"text_name": "3.markdown", | ||||||
|  | 			"binary_name": "" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user
	 Timur Ismagilov
					Timur Ismagilov