diff --git a/assets/assets.qtpl b/assets/assets.qtpl
deleted file mode 100644
index 5330a7a..0000000
--- a/assets/assets.qtpl
+++ /dev/null
@@ -1,38 +0,0 @@
-{%- func HelpMessage() -%}
-Usage of %s:
-{%- endfunc -%}
-
-{%- func ExampleConfig() -%}
-{% cat "config.ini" %}
-{%- endfunc -%}
-
-{% func DefaultCSS() %}
-{% cat "default.css" %}
-{% endfunc %}
-
-{% func ToolbarJS() %}
-{% cat "toolbar.js" %}
-{% endfunc %}
-
-Next three are from https://remixicon.com/
-{% func IconHTTP() %}
-{% cat "icon/http-protocol-icon.svg" %}
-{% endfunc %}
-
-{% func IconGemini() %}
-{% cat "icon/gemini-protocol-icon.svg" %}
-{% endfunc %}
-
-{% func IconMailto() %}
-{% cat "icon/mailto-protocol-icon.svg" %}
-{% endfunc %}
-
-This is a modified version of https://www.svgrepo.com/svg/232085/rat
-{% func IconGopher() %}
-{% cat "icon/gopher-protocol-icon.svg" %}
-{% endfunc %}
-
-https://upload.wikimedia.org/wikipedia/commons/4/46/Generic_Feed-icon.svg
-{% func IconFeed() %}
-{% cat "icon/feed-icon.svg" %}
-{% endfunc %}
diff --git a/assets/assets.qtpl.go b/assets/assets.qtpl.go
deleted file mode 100644
index 71355e9..0000000
--- a/assets/assets.qtpl.go
+++ /dev/null
@@ -1,828 +0,0 @@
-// Code generated by qtc from "assets.qtpl". DO NOT EDIT.
-// See https://github.com/valyala/quicktemplate for details.
-
-//line assets/assets.qtpl:1
-package assets
-
-//line assets/assets.qtpl:1
-import (
- qtio422016 "io"
-
- qt422016 "github.com/valyala/quicktemplate"
-)
-
-//line assets/assets.qtpl:1
-var (
- _ = qtio422016.Copy
- _ = qt422016.AcquireByteBuffer
-)
-
-//line assets/assets.qtpl:1
-func StreamHelpMessage(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:1
- qw422016.N().S(`Usage of %s:
-`)
-//line assets/assets.qtpl:3
-}
-
-//line assets/assets.qtpl:3
-func WriteHelpMessage(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:3
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:3
- StreamHelpMessage(qw422016)
-//line assets/assets.qtpl:3
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:3
-}
-
-//line assets/assets.qtpl:3
-func HelpMessage() string {
-//line assets/assets.qtpl:3
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:3
- WriteHelpMessage(qb422016)
-//line assets/assets.qtpl:3
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:3
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:3
- return qs422016
-//line assets/assets.qtpl:3
-}
-
-//line assets/assets.qtpl:5
-func StreamExampleConfig(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:6
- qw422016.N().S(`WikiName = My wiki
-NaviTitleIcon = ๐
-
-[Hyphae]
-HomeHypha = home
-UserHypha = u
-HeaderLinksHypha = header-links
-
-[Network]
-HTTPPort = 8080
-URL = https://wiki
-GeminiCertificatePath = /home/wiki/gemcerts
-
-[Authorization]
-UseFixedAuth = true
-FixedAuthCredentialsPath = /home/wiki/mycocredentials.json
-
-UseRegistration = true
-RegistrationCredentialsPath = /home/wiki/mycoregistration.json
-LimitRegistration = 10
-`)
-//line assets/assets.qtpl:6
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:7
-}
-
-//line assets/assets.qtpl:7
-func WriteExampleConfig(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:7
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:7
- StreamExampleConfig(qw422016)
-//line assets/assets.qtpl:7
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:7
-}
-
-//line assets/assets.qtpl:7
-func ExampleConfig() string {
-//line assets/assets.qtpl:7
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:7
- WriteExampleConfig(qb422016)
-//line assets/assets.qtpl:7
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:7
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:7
- return qs422016
-//line assets/assets.qtpl:7
-}
-
-//line assets/assets.qtpl:9
-func StreamDefaultCSS(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:9
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:10
- qw422016.N().S(`.non-existent-hypha { }
-.non-existent-hypha__ways { display: flex; flex-direction: column; width: 100%; margin: 0 0 1rem 0;}
-.non-existent-hypha__way { border: 1px #999 solid; border-radius: .25rem; padding: .25rem; }
-.non-existent-hypha__title { margin-bottom: 1rem; }
-.non-existent-hypha__subtitle { margin: 0; }
-
-.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
-#upload-binary__input { display: block; margin: .25rem 0 .25rem 0; }
-
-.modal__title { font-size: 2rem; }
-.modal__title_small { font-size: 1.5rem; }
-.modal__confirmation-msg { margin: 0 0 .5rem 0; }
-
-.hypha-list { padding-left: 0; }
-.hypha-list__entry { list-style-type: none; }
-.hypha-list__link { text-decoration: none; display: inline-block; padding: .25rem; }
-.hypha-list__link:hover { text-decoration: underline; }
-.hypha-list__amnt-type { font-size: smaller; color: #999; }
-
-/* General element positions, from small to big */
-/* Phones and whatnot */
-.layout { display: grid; row-gap: 1rem; }
-header { width: 100%; margin-bottom: 1rem; }
-.header-links__list, .hypha-tabs__flex { margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
-.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
-
-.header-links__entry { margin-right: .5rem; }
-.header-links__entry_user, .header-links__entry_register { font-style:italic; }
-.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
-
-.hypha-tabs { padding: 0; margin: 0; }
-.hypha-tabs__tab { margin-right: .5rem; padding: 0; }
-.hypha-tabs__link { display: inline-block; padding: .25rem; text-decoration: none; }
-.hypha-tabs__selection { display: inline-block; padding: .25rem; font-weight: bold; }
-
-.layout-card li { list-style-type: none; }
-.backlinks__list { padding: 0; margin: 0; }
-.backlinks__link { text-decoration: none; display: block; padding: .25rem; padding-left: 1.25rem; }
-
-@media screen and (max-width: 800px) {
- .amnt-grid { grid-template-columns: 1fr; }
- .layout { grid-template-columns: auto; grid-template-rows: auto auto auto; }
- .main-width { width: 100%; }
- main { padding: 1rem; margin: 0; }
-}
-
-@media screen and (min-width: 500px) {
- .non-existent-hypha__way { flex: 1; margin-right: .5rem; }
- .non-existent-hypha__ways { flex-direction: row; }
- .non-existent-hypha__way:last-child { margin-right: 0; }
-}
-
-/* No longer a phone but still small screen: draw normal tabs, center main */
-@media screen and (min-width: 801px) {
- .main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
- main { border-radius: .25rem; }
- .layout-card { width: 800px; margin: 0 auto; }
-
- .header-links { padding: 0; }
- .header-links__entry { margin-right: 1.5rem; }
- .header-links__entry_user { margin: 0 2rem 0 auto; }
- .header-links__entry:nth-of-type(1),
-
- .hypha-tabs { padding: 0; }
- .hypha-tabs__tab { border-radius: .25rem .25rem 0 0; margin-right: 0; }
- .hypha-tabs__selection, .hypha-tabs__link { padding: .25rem .5rem; }
-
- .header-links__entry:nth-of-type(1), .hypha-tabs__tab:nth-of-type(1) { margin-left: 2rem; }
-}
-
-
-/* Wide enough to fit two columns ok */
-@media screen and (min-width: 1100px) {
- .layout { display: grid; grid-template-columns: auto 1fr; column-gap: 1rem; margin: 0 1rem; row-gap: 1rem; }
- .main-width { margin: 0; }
- main { grid-column: 1 / span 1; grid-row: 1 / span 2; }
- .relative-hyphae, .edit-toolbar { grid-column: 2 / span 1; grid-row: 1 / span 1; }
- .layout-card { width: 100%; }
- .edit-toolbar__buttons {display: grid; }
-}
-
-@media screen and (min-width: 1150px) {
- .edit-toolbar__buttons { grid-template-columns: 1fr 1fr; }
-}
-
-@media screen and (min-width: 1250px) {
- .layout { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
- .layout-card {max-width: 18rem;}
- .main-width { margin: 0 auto; }
- .backlinks { grid-column: 1 / span 1; margin-right: 0; }
- main { grid-column: 2 / span 1; }
- .relative-hyphae, .edit-toolbar { grid-column: 3 / span 1; margin-left: 0; }
- .edit-toolbar__buttons { grid-template-columns: 1fr; }
-
- .backlinks__title { text-align: right; }
- .backlinks__link { text-align: right; padding-right: 1.25rem; padding-left: .25rem; }
-}
-
-@media screen and (min-width: 1400px) {
- .edit-toolbar__buttons { grid-template-columns: 1fr 1fr; }
-}
-
-*, *::before, *::after {box-sizing: border-box;}
-html { height:100%; padding:0; }
-body {height:100%; margin:0; }
-body, input { font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
-main > form {margin-bottom:1rem;}
-textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
-
-.edit { min-height: 80vh; }
-.edit__title { margin-top: 0; }
-.edit__preview { border: 2px dashed #ddd; }
-.edit-form {height:70vh;}
-.edit-form textarea {width:100%;height:95%;}
-.edit-form__save { font-weight: bold; }
-.edit-toolbar__buttons, .edit-toolbar__ad { margin: .5rem; }
-
-.icon {margin-right: .25rem; vertical-align: bottom; }
-
-main h1:not(.navi-title) {font-size:1.7rem;}
-blockquote { margin: 0; padding-left: .75rem; }
-.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
-/* .wikilink_external { padding-left: 16px; } */
-.wikilink_gopher::before { content: url("/assets/icon/gopher"); }
-.wikilink_http::before { content: url("/assets/icon/http"); }
-.wikilink_https::before { content: url("/assets/icon/http"); }
-/* .wikilink_https { background: transparent url("/assets/icon/http") center left no-repeat; } */
-.wikilink_gemini::before { content: url("/assets/icon/gemini"); }
-.wikilink_mailto::before { content: url("/assets/icon/mailto"); }
-
-article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
-main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
-.heading__link { text-decoration: none; display: inline-block; }
-.heading__link::after { width: 1rem; content: "ยง"; color: transparent; }
-.heading__link:hover::after, .heading__link:active::after { color: #999; }
-article p { margin: .5rem 0; }
-article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
-article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; font-family: 'Menlo', 'PT Mono', monospace; }
-article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}
-.codeblock code {padding:0; font-size:15px;}
-.transclusion { border-radius: .25rem; }
-.transclusion__content > *:not(.binary-container) {margin: 0.5rem; }
-.transclusion__link {display: block; text-align: right; font-style: italic; margin-top: .5rem; margin-right: .25rem; text-decoration: none;}
-.transclusion__link::before {content: "โ ";}
-
-/* Derived from https://commons.wikimedia.org/wiki/File:U%2B21D2.svg */
-.launchpad__entry { list-style-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='25' height='12'%3E%3Cg transform='scale(0.7,0.8) translate(-613.21429,-421)'%3E%3Cpath fill='%23999' d='M 638.06773,429.49751 L 631.01022,436.87675 L 630.1898,436.02774 L 632.416,433.30375 L 613.46876,433.30375 L 613.46876,431.66382 L 633.82089,431.66382 L 635.57789,429.5261 L 633.79229,427.35979 L 613.46876,427.35979 L 613.46876,425.71985 L 632.416,425.71985 L 630.1898,422.99587 L 631.01022,422.08788 L 638.06773,429.49751 z '/%3E%3C/g%3E%3C/svg%3E"); }
-
-.binary-container { width: 100%; text-align: center; }
-.binary-container_with-img img,
-.binary-container_with-video video,
-.binary-container_with-audio audio { max-width: 100%; max-height: 30em; width: auto; }
-
-.subhyphae__title { padding-bottom: .5rem; clear: both; }
-.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
-.navi-title a {text-decoration:none; }
-.navi-title__separator { margin: 0 .25rem; }
-.navi-title__colon { margin-right: .5rem; }
-.upload-amnt { clear: both; padding: .5rem; border-radius: .25rem; }
-.upload-amnt__unattach { display: block; }
-aside { clear: both; }
-
-.img-gallery { text-align: center; margin-top: .25rem; margin-bottom: .25rem; }
-.img-gallery_many-images { border-radius: .25rem; padding: .5rem; }
-.img-gallery img { max-width: 100%; max-height: 50vh; }
-figure { margin: 0; }
-figcaption { padding-bottom: .5rem; }
-
-#new-name {width:100%;}
-
-
-.rc-entry { display: grid; list-style-type: none; padding: .25rem; grid-template-columns: 1fr 1fr; border-radius: .25rem; }
-.rc-entry__time { font-style: italic; }
-.rc-entry__hash { font-style: italic; text-align: right; }
-.rc-entry__links, .rc-entry__msg { grid-column: 1 / span 2; }
-.rc-entry__author { font-style: italic; }
-
-.prevnext__el { display: inline-block; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; max-width: 49%; }
-.prevnext__prev { float: left; }
-.prevnext__next { float: right; text-align: right; }
-
-.page-separator { clear: both; }
-.history__entries { background-color: #eee; margin: 0; padding: 0; border-radius: .25rem; }
-.history__month-anchor { text-decoration: none; color: inherit; }
-.history__entry { list-style-type: none; padding: .25rem; }
-.history-entry { padding: .25rem; }
-.history-entry__time { font-weight: bold; }
-.history-entry__author { font-style: italic; }
-
-table { border: #ddd 1px solid; border-radius: .25rem; min-width: 4rem; }
-td { padding: .25rem; }
-caption { caption-side: top; font-size: small; }
-
-.subhyphae__list, .subhyphae__list ul { display: flex; padding: 0; margin: 0; flex-wrap: wrap; }
-.subhyphae__list ul { font-size: 90%; }
-.subhyphae__entry { list-style-type: none; border: 1px solid #999; padding: 0; margin: .125rem; border-radius: .25rem; }
-.subhyphae__link { display: block; padding: .25rem; text-decoration: none; }
-.subhyphae__link:hover { background: #eee; }
-
-.relative-hyphae__list { padding: 0; margin: 0; }
-.relative-hyphae__entry { clear: both; }
-.relative-hyphae__count { display: inline-block; float: right; }
-.relative-hyphae__entry_this { padding: .25rem .5rem; font-weight: bold; }
-.relative-hyphae__link { text-decoration: none; display: block; padding: .25rem .5rem; }
-
-::-webkit-file-upload-button,
-.btn { line-height: normal; display: inline-block; border: 1px #999 solid; border-radius: .25rem; text-decoration: none; padding: .25rem; font-size: 1rem; margin: 0; }
-.btn_weak { border: 1px #999 dashed; }
-
-/* Color stuff */
-/* Lighter stuff #eee */
-::-webkit-file-upload-button, .btn { background-color: #eee; color: black; }
-.btn:visited { color: black; }
-.btn_weak { background-color: transparent; }
-
-article code,
-article .codeblock,
-.transclusion,
-.img-gallery_many-images,
-.rc-entry,
-.prevnext__el,
-table { background-color: #eee; }
-
-.hypha-tabs__tab { background-color: #eee; }
-.hypha-tabs__tab a { color: black; }
-.hypha-tabs__tab_active { border-bottom: 2px white solid; background: white; }
-
-@media screen and (max-width: 800px) {
- .hypha-tabs,
- .hypha-tabs__tab { background-color: white; }
-}
-
-@media screen and (min-width: 801px) {
- .hypha-tabs__tab { border: 1px #ddd solid; }
- .hypha-tabs__tab_active { border-bottom: 1px white solid; }
-}
-
-.layout-card { border-radius: .25rem; background-color: white; }
-.layout-card__title { font-size: 1rem; margin: 0; padding: .25rem .5rem; border-radius: .25rem .25rem 0 0; }
-.layout-card__title { border-bottom: 1px solid #eee; }
-
-/* Other stuff */
-html { background-color: #eee;
-}
-header { background-color: #eee; }
-.header-links__link { color: black; }
-.header-links__link:hover { background-color: #ddd; }
-main { background-color: white; }
-
-blockquote { border-left: 2px #999 solid; }
-.wikilink_new {color:#a55858;}
-.transclusion code, .transclusion .codeblock {background-color:#ddd;}
-.transclusion__link { color: black; }
-.wikilink_new:visited {color:#a55858;}
-.navi-title { border-bottom: #eee 1px solid; }
-.upload-amnt { border: #eee 1px solid; }
-td { border: #ddd 1px solid; }
-
-.relative-hyphae__link:hover, .backlinks__link:hover { background-color: #eee; }
-
-/* Dark theme! */
-@media (prefers-color-scheme: dark) {
-html { background: #222; color: #ddd; }
-main, article, .hypha-tabs__tab, header, .layout-card { background-color: #343434; color: #ddd; }
-
-a, .wikilink_external { color: #f1fa8c; }
-a:visited, .wikilink_external:visited { color: #ffb86c; }
-.wikilink_new, .wikilink_new:visited { color: #dd4444; }
-.subhyphae__link:hover, .relative-hyphae__link:hover, .backlinks__link:hover { background-color: #444; }
-
-.header-links__link, .header-links__link:visited,
-.prevnext__el, .prevnext__el:visited { color: #ddd; }
-.header-links__link:hover { background-color: #444; }
-
-.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
-.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
-
-
-.transclusion .transclusion__link { color: #ddd; }
-
-input[type="text"], input[type="password"],
-::-webkit-file-upload-button,
-.btn,
-article code,
-article .codeblock,
-.transclusion,
-.img-gallery_many-images,
-.rc-entry,
-.history__entry,
-.prevnext__el,
-.upload-amnt,
-textarea,
-table { border: 0; background-color: #444444; color: #ddd; }
-.btn:visited { color: #ddd;}
-
- .btn { border: #444 solid 1px; border-radius: .25rem; }
- .btn_weak { background-color: transparent; }
-
-.transclusion code,
-.transclusion .codeblock { background-color: #454545; }
-mark { background: rgba(130, 80, 30, 5); color: inherit; }
-@media screen and (max-width: 800px) {
- .hypha-tabs { background-color: #232323; }
-}
-}
-
-
-`)
-//line assets/assets.qtpl:10
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:11
-}
-
-//line assets/assets.qtpl:11
-func WriteDefaultCSS(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:11
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:11
- StreamDefaultCSS(qw422016)
-//line assets/assets.qtpl:11
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:11
-}
-
-//line assets/assets.qtpl:11
-func DefaultCSS() string {
-//line assets/assets.qtpl:11
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:11
- WriteDefaultCSS(qb422016)
-//line assets/assets.qtpl:11
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:11
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:11
- return qs422016
-//line assets/assets.qtpl:11
-}
-
-//line assets/assets.qtpl:13
-func StreamToolbarJS(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:13
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:14
- qw422016.N().S(`const editTextarea = document.getElementsByClassName('edit-form__textarea')[0]
-
-function placeCursor(position, el = editTextarea) {
- el.selectionEnd = position
- el.selectionStart = el.selectionEnd
-}
-
-function getSelectedText(el = editTextarea) {
- const [start, end] = [el.selectionStart, el.selectionEnd]
- const text = el.value
- return text.substring(start, end)
-}
-
-function textInserter(text, cursorPosition = null, el = editTextarea) {
- return function() {
- const [start, end] = [el.selectionStart, el.selectionEnd]
- el.setRangeText(text, start, end, 'select')
- el.focus()
- if (cursorPosition == null) {
- placeCursor(end + text.length)
- } else {
- placeCursor(end + cursorPosition)
- }
- }
-}
-
-function selectionWrapper(cursorPosition, prefix, postfix = null, el = editTextarea) {
- return function() {
- const [start, end] = [el.selectionStart, el.selectionEnd]
- if (postfix == null) {
- postfix = prefix
- }
- let text = getSelectedText(el)
- let result = prefix + text + postfix
- el.setRangeText(result, start, end, 'select')
- el.focus()
- placeCursor(end + cursorPosition)
- }
-}
-
-const wrapBold = selectionWrapper(2, '**'),
- wrapItalic = selectionWrapper(2, '//'),
- wrapMonospace = selectionWrapper(1, '`)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(`'),
- wrapHighlighted = selectionWrapper(2, '!!'),
- wrapLifted = selectionWrapper(2, '^^'),
- wrapLowered = selectionWrapper(2, ',,'),
- wrapStrikethrough = selectionWrapper(2, '~~'),
- wrapLink = selectionWrapper(2, '[[', ']]')
-
-const insertHorizontalBar = textInserter('\n----\n'),
- insertImgBlock = textInserter('\nimg {\n \n}\n', 10),
- insertTableBlock = textInserter('\ntable {\n \n}\n', 12),
- insertRocket = textInserter('\n=> '),
- insertXcl = textInserter('\n<= '),
- insertHeading2 = textInserter('\n## '),
- insertHeading3 = textInserter('\n### '),
- insertCodeblock = textInserter('\n`)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(``)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(``)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(`\n\n`)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(``)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(``)
-//line assets/assets.qtpl:14
- qw422016.N().S("`")
-//line assets/assets.qtpl:14
- qw422016.N().S(`\n', 5),
- insertBulletedList = textInserter('\n* '),
- insertNumberedList = textInserter('\n*. ')
-
-function insertDate() {
- let date = new Date().toISOString().split('T')[0]
- textInserter(date)()
-}
-
-function insertTimeUTC() {
- let time = new Date().toISOString().substring(11, 19) + " UTC"
- textInserter(time)()
-}
-
-function insertUserlink() {
- const userlink = document.querySelector('.header-links__entry_user a')
- const userHypha = userlink.getAttribute('href').substring(7) // no /hypha/
- textInserter('[[' + userHypha + ']]')()
-}
-`)
-//line assets/assets.qtpl:14
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:15
-}
-
-//line assets/assets.qtpl:15
-func WriteToolbarJS(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:15
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:15
- StreamToolbarJS(qw422016)
-//line assets/assets.qtpl:15
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:15
-}
-
-//line assets/assets.qtpl:15
-func ToolbarJS() string {
-//line assets/assets.qtpl:15
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:15
- WriteToolbarJS(qb422016)
-//line assets/assets.qtpl:15
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:15
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:15
- return qs422016
-//line assets/assets.qtpl:15
-}
-
-// Next three are from https://remixicon.com/
-
-//line assets/assets.qtpl:18
-func StreamIconHTTP(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:18
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:19
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:19
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:20
-}
-
-//line assets/assets.qtpl:20
-func WriteIconHTTP(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:20
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:20
- StreamIconHTTP(qw422016)
-//line assets/assets.qtpl:20
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:20
-}
-
-//line assets/assets.qtpl:20
-func IconHTTP() string {
-//line assets/assets.qtpl:20
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:20
- WriteIconHTTP(qb422016)
-//line assets/assets.qtpl:20
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:20
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:20
- return qs422016
-//line assets/assets.qtpl:20
-}
-
-//line assets/assets.qtpl:22
-func StreamIconGemini(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:22
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:23
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:23
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:24
-}
-
-//line assets/assets.qtpl:24
-func WriteIconGemini(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:24
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:24
- StreamIconGemini(qw422016)
-//line assets/assets.qtpl:24
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:24
-}
-
-//line assets/assets.qtpl:24
-func IconGemini() string {
-//line assets/assets.qtpl:24
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:24
- WriteIconGemini(qb422016)
-//line assets/assets.qtpl:24
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:24
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:24
- return qs422016
-//line assets/assets.qtpl:24
-}
-
-//line assets/assets.qtpl:26
-func StreamIconMailto(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:26
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:27
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:27
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:28
-}
-
-//line assets/assets.qtpl:28
-func WriteIconMailto(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:28
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:28
- StreamIconMailto(qw422016)
-//line assets/assets.qtpl:28
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:28
-}
-
-//line assets/assets.qtpl:28
-func IconMailto() string {
-//line assets/assets.qtpl:28
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:28
- WriteIconMailto(qb422016)
-//line assets/assets.qtpl:28
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:28
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:28
- return qs422016
-//line assets/assets.qtpl:28
-}
-
-// This is a modified version of https://www.svgrepo.com/svg/232085/rat
-
-//line assets/assets.qtpl:31
-func StreamIconGopher(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:31
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:32
- qw422016.N().S(`
-
-
-
-`)
-//line assets/assets.qtpl:32
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:33
-}
-
-//line assets/assets.qtpl:33
-func WriteIconGopher(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:33
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:33
- StreamIconGopher(qw422016)
-//line assets/assets.qtpl:33
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:33
-}
-
-//line assets/assets.qtpl:33
-func IconGopher() string {
-//line assets/assets.qtpl:33
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:33
- WriteIconGopher(qb422016)
-//line assets/assets.qtpl:33
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:33
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:33
- return qs422016
-//line assets/assets.qtpl:33
-}
-
-// https://upload.wikimedia.org/wikipedia/commons/4/46/Generic_Feed-icon.svg
-
-//line assets/assets.qtpl:36
-func StreamIconFeed(qw422016 *qt422016.Writer) {
-//line assets/assets.qtpl:36
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:37
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:37
- qw422016.N().S(`
-`)
-//line assets/assets.qtpl:38
-}
-
-//line assets/assets.qtpl:38
-func WriteIconFeed(qq422016 qtio422016.Writer) {
-//line assets/assets.qtpl:38
- qw422016 := qt422016.AcquireWriter(qq422016)
-//line assets/assets.qtpl:38
- StreamIconFeed(qw422016)
-//line assets/assets.qtpl:38
- qt422016.ReleaseWriter(qw422016)
-//line assets/assets.qtpl:38
-}
-
-//line assets/assets.qtpl:38
-func IconFeed() string {
-//line assets/assets.qtpl:38
- qb422016 := qt422016.AcquireByteBuffer()
-//line assets/assets.qtpl:38
- WriteIconFeed(qb422016)
-//line assets/assets.qtpl:38
- qs422016 := string(qb422016.B)
-//line assets/assets.qtpl:38
- qt422016.ReleaseByteBuffer(qb422016)
-//line assets/assets.qtpl:38
- return qs422016
-//line assets/assets.qtpl:38
-}
diff --git a/flag.go b/flag.go
index 19c69d6..872f44c 100644
--- a/flag.go
+++ b/flag.go
@@ -1,18 +1,20 @@
package main
import (
+ _ "embed"
"flag"
"fmt"
"github.com/bouncepaw/mycorrhiza/cfg"
"log"
"os"
"path/filepath"
-
- "github.com/bouncepaw/mycorrhiza/assets"
)
// CLI options are read and parsed here.
+//go:embed assets/config.ini
+var defaultConfig []byte
+
var printExampleConfig bool
func init() {
@@ -21,11 +23,11 @@ func init() {
flag.Usage = printHelp
}
-// printHelp prints the help message. The help message is stored in assets.
+// printHelp prints the help message.
func printHelp() {
_, err := fmt.Fprintf(
flag.CommandLine.Output(),
- assets.HelpMessage(),
+ "Usage of %s:\n",
os.Args[0],
)
if err != nil {
@@ -40,7 +42,7 @@ func parseCliArgs() {
args := flag.Args()
if printExampleConfig {
- fmt.Printf(assets.ExampleConfig())
+ os.Stdout.Write(defaultConfig)
os.Exit(0)
}
diff --git a/go.mod b/go.mod
index e9297ed..109d91a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/bouncepaw/mycorrhiza
-go 1.14
+go 1.16
require (
git.sr.ht/~adnano/go-gemini v0.1.13
diff --git a/main.go b/main.go
index 33e17f9..5f1cb7e 100644
--- a/main.go
+++ b/main.go
@@ -11,6 +11,7 @@ import (
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/shroom"
+ "github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/web"
"log"
@@ -40,6 +41,9 @@ func main() {
history.Start()
shroom.SetHeaderLinks()
+ // Static files:
+ static.InitFS(cfg.WikiDir + "/static")
+
// Network:
go handleGemini()
web.Init()
diff --git a/assets/default.css b/static/default.css
similarity index 96%
rename from assets/default.css
rename to static/default.css
index 66d41a4..8ab0fed 100644
--- a/assets/default.css
+++ b/static/default.css
@@ -121,12 +121,11 @@ main h1:not(.navi-title) {font-size:1.7rem;}
blockquote { margin: 0; padding-left: .75rem; }
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
/* .wikilink_external { padding-left: 16px; } */
-.wikilink_gopher::before { content: url("/assets/icon/gopher"); }
-.wikilink_http::before { content: url("/assets/icon/http"); }
-.wikilink_https::before { content: url("/assets/icon/http"); }
-/* .wikilink_https { background: transparent url("/assets/icon/http") center left no-repeat; } */
-.wikilink_gemini::before { content: url("/assets/icon/gemini"); }
-.wikilink_mailto::before { content: url("/assets/icon/mailto"); }
+.wikilink_gopher::before { content: url("/static/icon/gopher-proto.svg"); }
+.wikilink_http::before, .wikilink_https::before { content: url("/static/icon/http-proto.svg"); }
+/* .wikilink_https { background: transparent url("/static/icon/http-proto.svg") center left no-repeat; } */
+.wikilink_gemini::before { content: url("/static/icon/gemini-proto.svg"); }
+.wikilink_mailto::before { content: url("/static/icon/mailto-proto.svg"); }
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
diff --git a/static/icon/README.md b/static/icon/README.md
new file mode 100644
index 0000000..676093a
--- /dev/null
+++ b/static/icon/README.md
@@ -0,0 +1,10 @@
+# Icons
+
+#### `{http,gemini,mailto}-proto`
+These are from https://remixicon.com/.
+
+#### `gopher-proto`
+This is a modified version of https://www.svgrepo.com/svg/232085/rat.
+
+#### `feed`
+This one is from https://upload.wikimedia.org/wikipedia/commons/4/46/Generic_Feed-icon.svg.
diff --git a/assets/icon/feed-icon.svg b/static/icon/feed.svg
similarity index 100%
rename from assets/icon/feed-icon.svg
rename to static/icon/feed.svg
diff --git a/assets/icon/gemini-protocol-icon.svg b/static/icon/gemini-proto.svg
similarity index 100%
rename from assets/icon/gemini-protocol-icon.svg
rename to static/icon/gemini-proto.svg
diff --git a/assets/icon/gopher-protocol-icon.svg b/static/icon/gopher-proto.svg
similarity index 100%
rename from assets/icon/gopher-protocol-icon.svg
rename to static/icon/gopher-proto.svg
diff --git a/assets/icon/http-protocol-icon.svg b/static/icon/http-proto.svg
similarity index 100%
rename from assets/icon/http-protocol-icon.svg
rename to static/icon/http-proto.svg
diff --git a/assets/icon/mailto-protocol-icon.svg b/static/icon/mailto-proto.svg
similarity index 100%
rename from assets/icon/mailto-protocol-icon.svg
rename to static/icon/mailto-proto.svg
diff --git a/static/static.go b/static/static.go
new file mode 100644
index 0000000..f36ee71
--- /dev/null
+++ b/static/static.go
@@ -0,0 +1,50 @@
+package static
+
+import (
+ "embed"
+ "io/fs"
+ "log"
+ "os"
+)
+
+//go:embed *.css *.js icon
+var embedFS embed.FS
+
+// FS serves all static files.
+var FS HybridFS
+
+// HybridFS is a filesystem that implements fs.FS. It can serve files
+// from multiple filesystems, falling back on failures.
+type HybridFS struct {
+ fs []fs.FS
+}
+
+// Open tries to open the requested file using all filesystems provided.
+// If neither succeeds, it returns the last error.
+func (f HybridFS) Open(name string) (fs.File, error) {
+ log.Printf("serving static file: %s\n", name)
+
+ var file fs.File
+ var err error
+
+ for _, candidate := range f.fs {
+ file, err = candidate.Open(name)
+ if err == nil {
+ log.Println("succeeded")
+ return file, nil
+ }
+ }
+
+ log.Printf("failed: %v\n", err)
+ return nil, err
+}
+
+// InitFS initializes the global HybridFS singleton with the local wiki.
+func InitFS(localPath string) {
+ FS = HybridFS{
+ fs: []fs.FS{
+ os.DirFS(localPath),
+ embedFS,
+ },
+ }
+}
diff --git a/assets/toolbar.js b/static/toolbar.js
similarity index 100%
rename from assets/toolbar.js
rename to static/toolbar.js
diff --git a/views/history.qtpl b/views/history.qtpl
index 7049261..7b3cce4 100644
--- a/views/history.qtpl
+++ b/views/history.qtpl
@@ -45,7 +45,7 @@ if err != nil {
recent changes
-
Subscribe via RSS , Atom or JSON feed .
+ Subscribe via RSS , Atom or JSON feed .
{% comment %}
Here I am, willing to add some accessibility using ARIA. Turns out,
diff --git a/views/history.qtpl.go b/views/history.qtpl.go
index 0a0462b..50cbad2 100644
--- a/views/history.qtpl.go
+++ b/views/history.qtpl.go
@@ -163,7 +163,7 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
recent changes
- Subscribe via RSS , Atom or JSON feed .
+ Subscribe via RSS , Atom or JSON feed .
`)
//line views/history.qtpl:55
diff --git a/views/mutators.qtpl b/views/mutators.qtpl
index 5b1c321..1ab396e 100644
--- a/views/mutators.qtpl
+++ b/views/mutators.qtpl
@@ -65,7 +65,7 @@
{% endif %}
-
+
{% endfunc %}
{% func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) %}
diff --git a/views/mutators.qtpl.go b/views/mutators.qtpl.go
index 95ac60c..8c36ceb 100644
--- a/views/mutators.qtpl.go
+++ b/views/mutators.qtpl.go
@@ -141,7 +141,7 @@ func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
qw422016.N().S(`
-
+
`)
//line views/mutators.qtpl:69
}
diff --git a/views/stuff.qtpl b/views/stuff.qtpl
index 26117af..b3b359f 100644
--- a/views/stuff.qtpl
+++ b/views/stuff.qtpl
@@ -8,10 +8,11 @@
-
-
+
{%s title %}
+
+
{% for _, el := range headElements %}{%s= el %}{% endfor %}
diff --git a/views/stuff.qtpl.go b/views/stuff.qtpl.go
index 9079a25..63a6cb0 100644
--- a/views/stuff.qtpl.go
+++ b/views/stuff.qtpl.go
@@ -39,22 +39,23 @@ func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User,
-
-
+
`)
-//line views/stuff.qtpl:14
+//line views/stuff.qtpl:13
qw422016.E().S(title)
-//line views/stuff.qtpl:14
+//line views/stuff.qtpl:13
qw422016.N().S(`
+
+
`)
-//line views/stuff.qtpl:15
+//line views/stuff.qtpl:16
for _, el := range headElements {
-//line views/stuff.qtpl:15
+//line views/stuff.qtpl:16
qw422016.N().S(el)
-//line views/stuff.qtpl:15
+//line views/stuff.qtpl:16
}
-//line views/stuff.qtpl:15
+//line views/stuff.qtpl:16
qw422016.N().S(`
@@ -62,81 +63,81 @@ func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User,
`)
-//line views/stuff.qtpl:28
+//line views/stuff.qtpl:29
qw422016.N().S(body)
-//line views/stuff.qtpl:28
+//line views/stuff.qtpl:29
qw422016.N().S(`
`)
-//line views/stuff.qtpl:29
+//line views/stuff.qtpl:30
streamomnipresentScripts(qw422016)
-//line views/stuff.qtpl:29
+//line views/stuff.qtpl:30
qw422016.N().S(`
`)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
}
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, u *user.User, headElements ...string) {
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
StreamBaseHTML(qw422016, title, body, u, headElements...)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
}
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
func BaseHTML(title, body string, u *user.User, headElements ...string) string {
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
WriteBaseHTML(qb422016, title, body, u, headElements...)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
return qs422016
-//line views/stuff.qtpl:32
+//line views/stuff.qtpl:33
}
-//line views/stuff.qtpl:34
+//line views/stuff.qtpl:35
func StreamUserListHTML(qw422016 *qt422016.Writer) {
-//line views/stuff.qtpl:34
+//line views/stuff.qtpl:35
qw422016.N().S(`
List of users
`)
-//line views/stuff.qtpl:39
+//line views/stuff.qtpl:40
var (
admins = make([]string, 0)
moderators = make([]string, 0)
@@ -153,303 +154,303 @@ func StreamUserListHTML(qw422016 *qt422016.Writer) {
}
}
-//line views/stuff.qtpl:54
+//line views/stuff.qtpl:55
qw422016.N().S(`
`)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
}
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
func WriteUserListHTML(qq422016 qtio422016.Writer) {
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
StreamUserListHTML(qw422016)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
}
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
func UserListHTML() string {
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
WriteUserListHTML(qb422016)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
return qs422016
-//line views/stuff.qtpl:75
+//line views/stuff.qtpl:76
}
-//line views/stuff.qtpl:77
+//line views/stuff.qtpl:78
func StreamHyphaListHTML(qw422016 *qt422016.Writer) {
-//line views/stuff.qtpl:77
+//line views/stuff.qtpl:78
qw422016.N().S(`
List of hyphae
This wiki has `)
-//line views/stuff.qtpl:81
+//line views/stuff.qtpl:82
qw422016.N().D(hyphae.Count())
-//line views/stuff.qtpl:81
+//line views/stuff.qtpl:82
qw422016.N().S(` hyphae.
`)
-//line views/stuff.qtpl:83
+//line views/stuff.qtpl:84
for h := range hyphae.YieldExistingHyphae() {
-//line views/stuff.qtpl:83
+//line views/stuff.qtpl:84
qw422016.N().S(`
`)
-//line views/stuff.qtpl:85
+//line views/stuff.qtpl:86
qw422016.E().S(util.BeautifulName(h.Name))
-//line views/stuff.qtpl:85
+//line views/stuff.qtpl:86
qw422016.N().S(`
`)
-//line views/stuff.qtpl:86
+//line views/stuff.qtpl:87
if h.BinaryPath != "" {
-//line views/stuff.qtpl:86
+//line views/stuff.qtpl:87
qw422016.N().S(`
`)
-//line views/stuff.qtpl:87
+//line views/stuff.qtpl:88
qw422016.E().S(filepath.Ext(h.BinaryPath)[1:])
-//line views/stuff.qtpl:87
+//line views/stuff.qtpl:88
qw422016.N().S(`
`)
-//line views/stuff.qtpl:88
+//line views/stuff.qtpl:89
}
-//line views/stuff.qtpl:88
+//line views/stuff.qtpl:89
qw422016.N().S(`
`)
-//line views/stuff.qtpl:90
+//line views/stuff.qtpl:91
}
-//line views/stuff.qtpl:90
+//line views/stuff.qtpl:91
qw422016.N().S(`
`)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
}
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
func WriteHyphaListHTML(qq422016 qtio422016.Writer) {
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
StreamHyphaListHTML(qw422016)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
}
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
func HyphaListHTML() string {
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
WriteHyphaListHTML(qb422016)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
return qs422016
-//line views/stuff.qtpl:94
+//line views/stuff.qtpl:95
}
-//line views/stuff.qtpl:96
+//line views/stuff.qtpl:97
func StreamAboutHTML(qw422016 *qt422016.Writer) {
-//line views/stuff.qtpl:96
+//line views/stuff.qtpl:97
qw422016.N().S(`
About `)
-//line views/stuff.qtpl:100
+//line views/stuff.qtpl:101
qw422016.E().S(cfg.WikiName)
-//line views/stuff.qtpl:100
+//line views/stuff.qtpl:101
qw422016.N().S(`
See /list for information about hyphae on this wiki.
`)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
}
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
func WriteAboutHTML(qq422016 qtio422016.Writer) {
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
StreamAboutHTML(qw422016)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
}
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
func AboutHTML() string {
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
WriteAboutHTML(qb422016)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
return qs422016
-//line views/stuff.qtpl:118
+//line views/stuff.qtpl:119
}
-//line views/stuff.qtpl:120
+//line views/stuff.qtpl:121
func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
-//line views/stuff.qtpl:120
+//line views/stuff.qtpl:121
qw422016.N().S(`
@@ -486,80 +487,80 @@ func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
`)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
}
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
func WriteAdminPanelHTML(qq422016 qtio422016.Writer) {
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
StreamAdminPanelHTML(qw422016)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
}
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
func AdminPanelHTML() string {
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
WriteAdminPanelHTML(qb422016)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
return qs422016
-//line views/stuff.qtpl:155
+//line views/stuff.qtpl:156
}
-//line views/stuff.qtpl:157
+//line views/stuff.qtpl:158
func streamomnipresentScripts(qw422016 *qt422016.Writer) {
-//line views/stuff.qtpl:157
+//line views/stuff.qtpl:158
qw422016.N().S(`
`)
-//line views/stuff.qtpl:158
+//line views/stuff.qtpl:159
for _, scriptPath := range cfg.OmnipresentScripts {
-//line views/stuff.qtpl:158
+//line views/stuff.qtpl:159
qw422016.N().S(`
`)
-//line views/stuff.qtpl:160
+//line views/stuff.qtpl:161
}
-//line views/stuff.qtpl:160
+//line views/stuff.qtpl:161
qw422016.N().S(`
`)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
}
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
func writeomnipresentScripts(qq422016 qtio422016.Writer) {
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
qw422016 := qt422016.AcquireWriter(qq422016)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
streamomnipresentScripts(qw422016)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
qt422016.ReleaseWriter(qw422016)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
}
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
func omnipresentScripts() string {
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
qb422016 := qt422016.AcquireByteBuffer()
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
writeomnipresentScripts(qb422016)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
qs422016 := string(qb422016.B)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
qt422016.ReleaseByteBuffer(qb422016)
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
return qs422016
-//line views/stuff.qtpl:161
+//line views/stuff.qtpl:162
}
diff --git a/web/web.go b/web/web.go
index 95b3c44..ed0ca78 100644
--- a/web/web.go
+++ b/web/web.go
@@ -6,20 +6,20 @@ package web
import (
"fmt"
"io"
- "io/ioutil"
"log"
+ "mime"
"net/http"
"net/url"
- "os"
- "strings"
- "github.com/bouncepaw/mycorrhiza/assets"
"github.com/bouncepaw/mycorrhiza/cfg"
+ "github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
+var stylesheets = []string{"default.css", "custom.css"}
+
// httpErr is used by many handlers to signal errors in a compact way.
func httpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
log.Println(errMsg, "for", name)
@@ -41,51 +41,16 @@ func httpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
- if _, err := os.Stat(cfg.WikiDir + "/assets/common.css"); err == nil {
- http.ServeFile(w, rq, cfg.WikiDir+"/assets/common.css")
- } else {
- w.Header().Set("Content-Type", "text/css;charset=utf-8")
- w.Write([]byte(assets.DefaultCSS()))
- }
- if bytes, err := ioutil.ReadFile(cfg.WikiDir + "/assets/custom.css"); err == nil {
- w.Write(bytes)
- }
-}
-func handlerToolbar(w http.ResponseWriter, rq *http.Request) {
- util.PrepareRq(rq)
- w.Header().Set("Content-Type", "text/javascript;charset=utf-8")
- w.Write([]byte(assets.ToolbarJS()))
-}
-
-// handlerIcon serves the requested icon. All icons are distributed as part of the Mycorrhiza binary.
-//
-// See assets/assets/icon/ for icons themselves, see assets/assets.qtpl for their sources.
-func handlerIcon(w http.ResponseWriter, rq *http.Request) {
- iconName := strings.TrimPrefix(rq.URL.Path, "/assets/icon/")
- if iconName == "https" {
- iconName = "http"
- }
- w.Header().Set("Content-Type", "image/svg+xml")
- icon := func() string {
- switch iconName {
- case "gemini":
- return assets.IconGemini()
- case "mailto":
- return assets.IconMailto()
- case "gopher":
- return assets.IconGopher()
- case "feed":
- return assets.IconFeed()
- default:
- return assets.IconHTTP()
+ w.Header().Set("Content-Type", mime.TypeByExtension("css"))
+ for _, name := range stylesheets {
+ file, err := static.FS.Open(name)
+ if err != nil {
+ continue
}
- }()
- _, err := io.WriteString(w, icon)
- if err != nil {
- log.Println(err)
+ io.Copy(w, file)
+ file.Close()
}
-
}
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
@@ -113,15 +78,15 @@ func Init() {
initHistory()
initStuff()
+ // Miscellaneous
http.HandleFunc("/user-list/", handlerUserList)
- http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(cfg.WikiDir+"/static"))))
- http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
- http.ServeFile(w, rq, cfg.WikiDir+"/static/favicon.ico")
- })
- http.HandleFunc("/assets/common.css", handlerStyle)
- http.HandleFunc("/assets/toolbar.js", handlerToolbar)
- http.HandleFunc("/assets/icon/", handlerIcon)
http.HandleFunc("/robots.txt", handlerRobotsTxt)
+
+ // Static assets
+ http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(static.FS))))
+ http.HandleFunc("/static/style.css", handlerStyle)
+
+ // Index page
http.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
addr, _ := url.Parse("/hypha/" + cfg.HomeHypha) // Let's pray it never fails
rq.URL = addr