~bouncepaw/mycorrhiza-devel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH v2] Migrate /edit to html/template

Details
Message ID
<20220819192459.6863-1-umar@handlerug.me>
DKIM signature
missing
Download raw message
Patch: +209 -629
---
Changes since v1:
- "You are creating a new hypha" Russian localization fix

I've explored different ways of refactoring the toolbar button code, but
they all looked far worse than the markup in the first version (weird
template functions and map generators, or a run-time template
generator), so I decided to leave it in. It's not actually solvable
using the text/template syntax, which seems to be by design because
templates should be simple and not contain code.

 hypview/hypview.go       |  64 +++++-
 hypview/mutators.qtpl    | 123 -----------
 hypview/mutators.qtpl.go | 452 ---------------------------------------
 hypview/view_edit.html   | 104 +++++++++
 static/default.css       |   2 +-
 viewutil/viewutil.go     |   7 +-
 web/mutators.go          |  86 ++++----
 7 files changed, 209 insertions(+), 629 deletions(-)
 delete mode 100644 hypview/mutators.qtpl
 delete mode 100644 hypview/mutators.qtpl.go
 create mode 100644 hypview/view_edit.html

diff --git a/hypview/hypview.go b/hypview/hypview.go
index 216aebc..d284c37 100644
--- a/hypview/hypview.go
+++ b/hypview/hypview.go
@@ -2,16 +2,51 @@ package hypview

import (
	"embed"
	"github.com/bouncepaw/mycorrhiza/cfg"
	"github.com/bouncepaw/mycorrhiza/viewutil"
	"html/template"
	"log"
	"strings"

	"github.com/bouncepaw/mycorrhiza/cfg"
	"github.com/bouncepaw/mycorrhiza/viewutil"
)

var (
	//go:embed *.html
	fs            embed.FS
	ruTranslation = `
{{define "editing hypha"}}Редактирование {{beautifulName .}}{{end}}
{{define "editing [[hypha]]"}}Редактирование <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}
{{define "you're creating a new hypha"}}Вы создаёте новую гифу.{{end}}
{{define "describe your changes"}}Опишите ваши правки{{end}}
{{define "save"}}Сохранить{{end}}
{{define "preview"}}Предпросмотр{{end}}
{{define "previewing hypha"}}Предпросмотр «{{beautifulName .}}»{{end}}
{{define "preview tip"}}Заметьте, эта гифа ещё не сохранена. Вот её предпросмотр:{{end}}

{{define "markup"}}Разметка{{end}}
{{define "link"}}Ссылка{{end}}
{{define "link title"}}Текст{{end}}
{{define "heading"}}Заголовок{{end}}
{{define "bold"}}Жирный{{end}}
{{define "italic"}}Курсив{{end}}
{{define "highlight"}}Выделение{{end}}
{{define "underline"}}Подчеркивание{{end}}
{{define "mono"}}Моноширинный{{end}}
{{define "super"}}Надстрочный{{end}}
{{define "sub"}}Подстрочный{{end}}
{{define "strike"}}Зачёркнутый{{end}}
{{define "rocket"}}Ссылка-ракета{{end}}
{{define "transclude"}}Трансклюзия{{end}}
{{define "hr"}}Гориз. черта{{end}}
{{define "code"}}Код-блок{{end}}
{{define "bullets"}}Маркир. список{{end}}
{{define "numbers"}}Нумер. список{{end}}
{{define "mycomarkup help"}}<a href="/help/en/mycomarkup" class="shy-link">Подробнее</a> о микоразметке{{end}}
{{define "actions"}}Действия{{end}}
{{define "current date"}}Текущая дата{{end}}
{{define "current time"}}Текущее время{{end}}
{{define "selflink"}}Ссылка на вас{{end}}

{{define "empty heading"}}Эта гифа не существует{{end}}
{{define "empty no rights"}}У вас нет прав для создания новых гиф. Вы можете:{{end}}
{{define "empty log in"}}Войти в свою учётную запись, если она у вас есть{{end}}
@@ -37,6 +72,7 @@ var (
{{define "leave redirections"}}Оставить перенаправления{{end}}
`
	chainNaviTitle   viewutil.Chain
	chainEditHypha   viewutil.Chain
	chainEmptyHypha  viewutil.Chain
	chainDeleteHypha viewutil.Chain
	chainRenameHypha viewutil.Chain
@@ -44,11 +80,35 @@ var (

func Init() {
	chainNaviTitle = viewutil.CopyEnRuWith(fs, "view_navititle.html", "")
	chainEditHypha = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
	chainEmptyHypha = viewutil.CopyEnRuWith(fs, "view_empty_hypha.html", ruTranslation)
	chainDeleteHypha = viewutil.CopyEnRuWith(fs, "view_delete.html", ruTranslation)
	chainRenameHypha = viewutil.CopyEnRuWith(fs, "view_rename.html", ruTranslation)
}

type editData struct {
	*viewutil.BaseData
	HyphaName string
	IsNew     bool
	Content   string
	Message   string
	Preview   template.HTML
}

func EditHypha(meta viewutil.Meta, hyphaName string, isNew bool, content string, message string, preview template.HTML) {
	viewutil.ExecutePage(meta, chainEditHypha, editData{
		BaseData: &viewutil.BaseData{
			Addr:        "/edit/" + hyphaName,
			EditScripts: cfg.EditScripts,
		},
		HyphaName: hyphaName,
		IsNew:     isNew,
		Content:   content,
		Message:   message,
		Preview:   preview,
	})
}

type deleteRenameData struct {
	*viewutil.BaseData
	HyphaName string
diff --git a/hypview/mutators.qtpl b/hypview/mutators.qtpl
deleted file mode 100644
index bab477d..0000000
--- a/hypview/mutators.qtpl
@@ -1,123 +0,0 @@
{% import "fmt" %}
{% import "net/http" %}

{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}

{% func Toolbar(u *user.User, lc *l18n.Localizer) %}
<aside class="edit-toolbar markup-toolbar layout-card">
	<h2 class="edit-toolbar__title layout-card__title">{%s lc.Get("edit.markup")%}</h2>
	<section class="edit-toolbar__buttons">
	{% for _, el := range []struct{
			class string
			display string
		}{
			{"link", fmt.Sprintf("[[%s]]", lc.Get("edit.link"))},
			{"titlelink", fmt.Sprintf("[[%s | %s]]", lc.Get("edit.link"), lc.Get("edit.link_title"))},
			{"heading1", fmt.Sprintf("= %s", lc.Get("edit.heading"))},
			{"heading2", fmt.Sprintf("== %s", lc.Get("edit.heading"))},
			{"bold", fmt.Sprintf("<b>**%s**</b>", lc.Get("edit.bold"))},
			{"italic", fmt.Sprintf("<i>//%s//</i>", lc.Get("edit.italic"))},
			{"highlighted", fmt.Sprintf("<mark>++%s++</mark>", lc.Get("edit.highlight"))},
			{"underline", fmt.Sprintf("<u>__%s__</u>", lc.Get("edit.underline"))},
			{"monospace", fmt.Sprintf("<code>`%s`</code>", lc.Get("edit.mono"))},
			{"lifted", fmt.Sprintf("<sup>^^%s^^</sup>", lc.Get("edit.super"))}, // inconsistent names: lifted, supertext. How cute ❤️
			{"lowered", fmt.Sprintf("<sub>,,%s,,</sub>", lc.Get("edit.sub"))},
			{"strikethrough", fmt.Sprintf("<strike>~~%s~~</strike>", lc.Get("edit.strike"))},
			{"rocket", "=> " + lc.Get("edit.rocket")},
			{"xcl", "<= " + lc.Get("edit.transclude")},
			{"img", "<code>img {}</code>"},
			{"table", "<code>table {}</code>"},
			{"hr", lc.Get("edit.hr")},
			{"codeblock", lc.Get("edit.code")},
			{"bulletedlist", "* " + lc.Get("edit.bullets")},
			{"numberedlist", "*. " + lc.Get("edit.numbers")},
		} %}
		<button class="btn edit-toolbar__btn edit-toolbar__{%s el.class %}">
			{%s= el.display %}
		</button>
	{% endfor %}
	</section>
	<p class="edit-toolbar__ad">{%s= lc.Get("edit.help", &l18n.Replacements{"link": fmt.Sprintf("<a href=\"/help/en/mycomarkup\" target=\"_blank\" class=\"shy-link\">%s</a>", lc.Get("edit.help_link"))}) %}</p>
</aside>
<aside class="edit-toolbar action-toolbar layout-card">
	<h2 class="edit-toolbar__title layout-card__title">{%s lc.Get("edit.actions")%}</h2>
	<section class="edit-toolbar__buttons">
	{% for _, el := range []struct{
			class string
			display string
		}{
			{"date", lc.Get("edit.date")},
			{"time", lc.Get("edit.time")},
		} %}
		<button class="btn edit-toolbar__btn edit-toolbar__{%s el.class %}">
			{%s= el.display %}
		</button>
	{% endfor %}
	{% if u.Group != "anon" %}
		<button class="btn edit-toolbar__btn edit-toolbar__user-link">
			{%s lc.Get("edit.selflink") %}
		</button>
	{% endif %}
	</section>
</aside>
<script src="/static/toolbar.js"></script>
{% endfunc %}

{% func Editor(rq *http.Request, hyphaName, textAreaFill, warning string) %}
{% code
	lc := l18n.FromRequest(rq)
%}
<main class="main-width edit edit_no-preview">
	<form method="post" class="edit-form"
			action="/upload-text/{%s hyphaName %}">
		<h1 class="edit__title">{%s= fmt.Sprintf(lc.Get("edit.title"), beautifulLink(hyphaName)) %}</h1>
		{%s= warning %}
		<textarea name="text" class="edit-form__textarea" autofocus>{%s textAreaFill %}</textarea>
		<p class="edit-form__message-zone">
			<input id="text" type="text" name="message" class="edit-form__message" placeholder="{%s lc.Get("edit.tag") %}" aria-label="{%s lc.Get("edit.tag") %}">
		</p>
		<p class="edit-form__buttons">
			<button type="submit" name="action" class="btn btn_accent edit-form__save" value="Save">{%s lc.Get("edit.save") %}</button>
			<button type="submit" name="action" class="btn edit-form__preview" value="Preview">{%s lc.Get("edit.preview") %}</button>
			<a href="/hypha/{%s hyphaName %}" class="btn btn_weak">{%s lc.Get("ui.cancel") %}</a>
		</p>
	</form>
</main>
{%s= Toolbar(user.FromRequest(rq), lc) %}
{%= editScripts() %}
{% endfunc %}

{% func Preview(rq *http.Request, hyphaName, textAreaFill, message, warning string, renderedPage string) %}
{% code
	lc := l18n.FromRequest(rq)
%}
<main class="main-width edit edit_with-preview">
	<form method="post" class="edit-form"
			action="/upload-text/{%s hyphaName %}">
		<h1 class="edit__title">{%s= fmt.Sprintf(lc.Get("edit.title"), beautifulLink(hyphaName)) %}</h1>
		{%s= warning %}
		<textarea name="text" class="edit-form__textarea" autofocus>{%s textAreaFill %}</textarea>
		<p class="edit-form__message-zone">
        	<input id="text" type="text" name="message" class="edit-form__message" placeholder="{%s lc.Get("edit.tag") %}" aria-label="{%s lc.Get("edit.tag") %}">
        </p>
		<p class="edit-form__buttons">
            <button type="submit" name="action" class="btn btn_accent edit-form__save" value="Save">{%s lc.Get("edit.save") %}</button>
            <button type="submit" name="action" class="btn edit-form__preview" value="Preview">{%s lc.Get("edit.preview") %}</button>
            <a href="/hypha/{%s hyphaName %}" class="btn btn_weak">{%s lc.Get("ui.cancel") %}</a>
        </p>
	</form>
	<p class="warning">{%s lc.Get("edit.preview_tip") %}</p>
	<article class="edit__preview">{%s= renderedPage %}</article>
</main>
{%s= Toolbar(user.FromRequest(rq), lc) %}
{%= editScripts() %}
{% endfunc %}

{% func editScripts() %}
<script src="/static/editor.js"></script>
{% for _, scriptPath := range cfg.EditScripts %}
<script src="{%s scriptPath %}"></script>
{% endfor %}
{% endfunc %}
diff --git a/hypview/mutators.qtpl.go b/hypview/mutators.qtpl.go
deleted file mode 100644
index 1200c9c..0000000
--- a/hypview/mutators.qtpl.go
@@ -1,452 +0,0 @@
// Code generated by qtc from "mutators.qtpl". DO NOT EDIT.
// See https://github.com/valyala/quicktemplate for details.

//line hypview/mutators.qtpl:1
package hypview

//line hypview/mutators.qtpl:1
import "fmt"

//line hypview/mutators.qtpl:2
import "net/http"

//line hypview/mutators.qtpl:4
import "github.com/bouncepaw/mycorrhiza/cfg"

//line hypview/mutators.qtpl:5
import "github.com/bouncepaw/mycorrhiza/l18n"

//line hypview/mutators.qtpl:6
import "github.com/bouncepaw/mycorrhiza/user"

//line hypview/mutators.qtpl:8
import (
	qtio422016 "io"

	qt422016 "github.com/valyala/quicktemplate"
)

//line hypview/mutators.qtpl:8
var (
	_ = qtio422016.Copy
	_ = qt422016.AcquireByteBuffer
)

//line hypview/mutators.qtpl:8
func StreamToolbar(qw422016 *qt422016.Writer, u *user.User, lc *l18n.Localizer) {
//line hypview/mutators.qtpl:8
	qw422016.N().S(`
<aside class="edit-toolbar markup-toolbar layout-card">
	<h2 class="edit-toolbar__title layout-card__title">`)
//line hypview/mutators.qtpl:10
	qw422016.E().S(lc.Get("edit.markup"))
//line hypview/mutators.qtpl:10
	qw422016.N().S(`</h2>
	<section class="edit-toolbar__buttons">
	`)
//line hypview/mutators.qtpl:12
	for _, el := range []struct {
		class   string
		display string
	}{
		{"link", fmt.Sprintf("[[%s]]", lc.Get("edit.link"))},
		{"titlelink", fmt.Sprintf("[[%s | %s]]", lc.Get("edit.link"), lc.Get("edit.link_title"))},
		{"heading1", fmt.Sprintf("= %s", lc.Get("edit.heading"))},
		{"heading2", fmt.Sprintf("== %s", lc.Get("edit.heading"))},
		{"bold", fmt.Sprintf("<b>**%s**</b>", lc.Get("edit.bold"))},
		{"italic", fmt.Sprintf("<i>//%s//</i>", lc.Get("edit.italic"))},
		{"highlighted", fmt.Sprintf("<mark>++%s++</mark>", lc.Get("edit.highlight"))},
		{"underline", fmt.Sprintf("<u>__%s__</u>", lc.Get("edit.underline"))},
		{"monospace", fmt.Sprintf("<code>`%s`</code>", lc.Get("edit.mono"))},
		{"lifted", fmt.Sprintf("<sup>^^%s^^</sup>", lc.Get("edit.super"))}, // inconsistent names: lifted, supertext. How cute ❤️
		{"lowered", fmt.Sprintf("<sub>,,%s,,</sub>", lc.Get("edit.sub"))},
		{"strikethrough", fmt.Sprintf("<strike>~~%s~~</strike>", lc.Get("edit.strike"))},
		{"rocket", "=> " + lc.Get("edit.rocket")},
		{"xcl", "<= " + lc.Get("edit.transclude")},
		{"img", "<code>img {}</code>"},
		{"table", "<code>table {}</code>"},
		{"hr", lc.Get("edit.hr")},
		{"codeblock", lc.Get("edit.code")},
		{"bulletedlist", "* " + lc.Get("edit.bullets")},
		{"numberedlist", "*. " + lc.Get("edit.numbers")},
	} {
//line hypview/mutators.qtpl:36
		qw422016.N().S(`
		<button class="btn edit-toolbar__btn edit-toolbar__`)
//line hypview/mutators.qtpl:37
		qw422016.E().S(el.class)
//line hypview/mutators.qtpl:37
		qw422016.N().S(`">
			`)
//line hypview/mutators.qtpl:38
		qw422016.N().S(el.display)
//line hypview/mutators.qtpl:38
		qw422016.N().S(`
		</button>
	`)
//line hypview/mutators.qtpl:40
	}
//line hypview/mutators.qtpl:40
	qw422016.N().S(`
	</section>
	<p class="edit-toolbar__ad">`)
//line hypview/mutators.qtpl:42
	qw422016.N().S(lc.Get("edit.help", &l18n.Replacements{"link": fmt.Sprintf("<a href=\"/help/en/mycomarkup\" target=\"_blank\" class=\"shy-link\">%s</a>", lc.Get("edit.help_link"))}))
//line hypview/mutators.qtpl:42
	qw422016.N().S(`</p>
</aside>
<aside class="edit-toolbar action-toolbar layout-card">
	<h2 class="edit-toolbar__title layout-card__title">`)
//line hypview/mutators.qtpl:45
	qw422016.E().S(lc.Get("edit.actions"))
//line hypview/mutators.qtpl:45
	qw422016.N().S(`</h2>
	<section class="edit-toolbar__buttons">
	`)
//line hypview/mutators.qtpl:47
	for _, el := range []struct {
		class   string
		display string
	}{
		{"date", lc.Get("edit.date")},
		{"time", lc.Get("edit.time")},
	} {
//line hypview/mutators.qtpl:53
		qw422016.N().S(`
		<button class="btn edit-toolbar__btn edit-toolbar__`)
//line hypview/mutators.qtpl:54
		qw422016.E().S(el.class)
//line hypview/mutators.qtpl:54
		qw422016.N().S(`">
			`)
//line hypview/mutators.qtpl:55
		qw422016.N().S(el.display)
//line hypview/mutators.qtpl:55
		qw422016.N().S(`
		</button>
	`)
//line hypview/mutators.qtpl:57
	}
//line hypview/mutators.qtpl:57
	qw422016.N().S(`
	`)
//line hypview/mutators.qtpl:58
	if u.Group != "anon" {
//line hypview/mutators.qtpl:58
		qw422016.N().S(`
		<button class="btn edit-toolbar__btn edit-toolbar__user-link">
			`)
//line hypview/mutators.qtpl:60
		qw422016.E().S(lc.Get("edit.selflink"))
//line hypview/mutators.qtpl:60
		qw422016.N().S(`
		</button>
	`)
//line hypview/mutators.qtpl:62
	}
//line hypview/mutators.qtpl:62
	qw422016.N().S(`
	</section>
</aside>
<script src="/static/toolbar.js"></script>
`)
//line hypview/mutators.qtpl:66
}

//line hypview/mutators.qtpl:66
func WriteToolbar(qq422016 qtio422016.Writer, u *user.User, lc *l18n.Localizer) {
//line hypview/mutators.qtpl:66
	qw422016 := qt422016.AcquireWriter(qq422016)
//line hypview/mutators.qtpl:66
	StreamToolbar(qw422016, u, lc)
//line hypview/mutators.qtpl:66
	qt422016.ReleaseWriter(qw422016)
//line hypview/mutators.qtpl:66
}

//line hypview/mutators.qtpl:66
func Toolbar(u *user.User, lc *l18n.Localizer) string {
//line hypview/mutators.qtpl:66
	qb422016 := qt422016.AcquireByteBuffer()
//line hypview/mutators.qtpl:66
	WriteToolbar(qb422016, u, lc)
//line hypview/mutators.qtpl:66
	qs422016 := string(qb422016.B)
//line hypview/mutators.qtpl:66
	qt422016.ReleaseByteBuffer(qb422016)
//line hypview/mutators.qtpl:66
	return qs422016
//line hypview/mutators.qtpl:66
}

//line hypview/mutators.qtpl:68
func StreamEditor(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
//line hypview/mutators.qtpl:68
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:70
	lc := l18n.FromRequest(rq)

//line hypview/mutators.qtpl:71
	qw422016.N().S(`
<main class="main-width edit edit_no-preview">
	<form method="post" class="edit-form"
			action="/upload-text/`)
//line hypview/mutators.qtpl:74
	qw422016.E().S(hyphaName)
//line hypview/mutators.qtpl:74
	qw422016.N().S(`">
		<h1 class="edit__title">`)
//line hypview/mutators.qtpl:75
	qw422016.N().S(fmt.Sprintf(lc.Get("edit.title"), beautifulLink(hyphaName)))
//line hypview/mutators.qtpl:75
	qw422016.N().S(`</h1>
		`)
//line hypview/mutators.qtpl:76
	qw422016.N().S(warning)
//line hypview/mutators.qtpl:76
	qw422016.N().S(`
		<textarea name="text" class="edit-form__textarea" autofocus>`)
//line hypview/mutators.qtpl:77
	qw422016.E().S(textAreaFill)
//line hypview/mutators.qtpl:77
	qw422016.N().S(`</textarea>
		<p class="edit-form__message-zone">
			<input id="text" type="text" name="message" class="edit-form__message" placeholder="`)
//line hypview/mutators.qtpl:79
	qw422016.E().S(lc.Get("edit.tag"))
//line hypview/mutators.qtpl:79
	qw422016.N().S(`" aria-label="`)
//line hypview/mutators.qtpl:79
	qw422016.E().S(lc.Get("edit.tag"))
//line hypview/mutators.qtpl:79
	qw422016.N().S(`">
		</p>
		<p class="edit-form__buttons">
			<button type="submit" name="action" class="btn btn_accent edit-form__save" value="Save">`)
//line hypview/mutators.qtpl:82
	qw422016.E().S(lc.Get("edit.save"))
//line hypview/mutators.qtpl:82
	qw422016.N().S(`</button>
			<button type="submit" name="action" class="btn edit-form__preview" value="Preview">`)
//line hypview/mutators.qtpl:83
	qw422016.E().S(lc.Get("edit.preview"))
//line hypview/mutators.qtpl:83
	qw422016.N().S(`</button>
			<a href="/hypha/`)
//line hypview/mutators.qtpl:84
	qw422016.E().S(hyphaName)
//line hypview/mutators.qtpl:84
	qw422016.N().S(`" class="btn btn_weak">`)
//line hypview/mutators.qtpl:84
	qw422016.E().S(lc.Get("ui.cancel"))
//line hypview/mutators.qtpl:84
	qw422016.N().S(`</a>
		</p>
	</form>
</main>
`)
//line hypview/mutators.qtpl:88
	qw422016.N().S(Toolbar(user.FromRequest(rq), lc))
//line hypview/mutators.qtpl:88
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:89
	streameditScripts(qw422016)
//line hypview/mutators.qtpl:89
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:90
}

//line hypview/mutators.qtpl:90
func WriteEditor(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
//line hypview/mutators.qtpl:90
	qw422016 := qt422016.AcquireWriter(qq422016)
//line hypview/mutators.qtpl:90
	StreamEditor(qw422016, rq, hyphaName, textAreaFill, warning)
//line hypview/mutators.qtpl:90
	qt422016.ReleaseWriter(qw422016)
//line hypview/mutators.qtpl:90
}

//line hypview/mutators.qtpl:90
func Editor(rq *http.Request, hyphaName, textAreaFill, warning string) string {
//line hypview/mutators.qtpl:90
	qb422016 := qt422016.AcquireByteBuffer()
//line hypview/mutators.qtpl:90
	WriteEditor(qb422016, rq, hyphaName, textAreaFill, warning)
//line hypview/mutators.qtpl:90
	qs422016 := string(qb422016.B)
//line hypview/mutators.qtpl:90
	qt422016.ReleaseByteBuffer(qb422016)
//line hypview/mutators.qtpl:90
	return qs422016
//line hypview/mutators.qtpl:90
}

//line hypview/mutators.qtpl:92
func StreamPreview(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, message, warning string, renderedPage string) {
//line hypview/mutators.qtpl:92
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:94
	lc := l18n.FromRequest(rq)

//line hypview/mutators.qtpl:95
	qw422016.N().S(`
<main class="main-width edit edit_with-preview">
	<form method="post" class="edit-form"
			action="/upload-text/`)
//line hypview/mutators.qtpl:98
	qw422016.E().S(hyphaName)
//line hypview/mutators.qtpl:98
	qw422016.N().S(`">
		<h1 class="edit__title">`)
//line hypview/mutators.qtpl:99
	qw422016.N().S(fmt.Sprintf(lc.Get("edit.title"), beautifulLink(hyphaName)))
//line hypview/mutators.qtpl:99
	qw422016.N().S(`</h1>
		`)
//line hypview/mutators.qtpl:100
	qw422016.N().S(warning)
//line hypview/mutators.qtpl:100
	qw422016.N().S(`
		<textarea name="text" class="edit-form__textarea" autofocus>`)
//line hypview/mutators.qtpl:101
	qw422016.E().S(textAreaFill)
//line hypview/mutators.qtpl:101
	qw422016.N().S(`</textarea>
		<p class="edit-form__message-zone">
        	<input id="text" type="text" name="message" class="edit-form__message" placeholder="`)
//line hypview/mutators.qtpl:103
	qw422016.E().S(lc.Get("edit.tag"))
//line hypview/mutators.qtpl:103
	qw422016.N().S(`" aria-label="`)
//line hypview/mutators.qtpl:103
	qw422016.E().S(lc.Get("edit.tag"))
//line hypview/mutators.qtpl:103
	qw422016.N().S(`">
        </p>
		<p class="edit-form__buttons">
            <button type="submit" name="action" class="btn btn_accent edit-form__save" value="Save">`)
//line hypview/mutators.qtpl:106
	qw422016.E().S(lc.Get("edit.save"))
//line hypview/mutators.qtpl:106
	qw422016.N().S(`</button>
            <button type="submit" name="action" class="btn edit-form__preview" value="Preview">`)
//line hypview/mutators.qtpl:107
	qw422016.E().S(lc.Get("edit.preview"))
//line hypview/mutators.qtpl:107
	qw422016.N().S(`</button>
            <a href="/hypha/`)
//line hypview/mutators.qtpl:108
	qw422016.E().S(hyphaName)
//line hypview/mutators.qtpl:108
	qw422016.N().S(`" class="btn btn_weak">`)
//line hypview/mutators.qtpl:108
	qw422016.E().S(lc.Get("ui.cancel"))
//line hypview/mutators.qtpl:108
	qw422016.N().S(`</a>
        </p>
	</form>
	<p class="warning">`)
//line hypview/mutators.qtpl:111
	qw422016.E().S(lc.Get("edit.preview_tip"))
//line hypview/mutators.qtpl:111
	qw422016.N().S(`</p>
	<article class="edit__preview">`)
//line hypview/mutators.qtpl:112
	qw422016.N().S(renderedPage)
//line hypview/mutators.qtpl:112
	qw422016.N().S(`</article>
</main>
`)
//line hypview/mutators.qtpl:114
	qw422016.N().S(Toolbar(user.FromRequest(rq), lc))
//line hypview/mutators.qtpl:114
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:115
	streameditScripts(qw422016)
//line hypview/mutators.qtpl:115
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:116
}

//line hypview/mutators.qtpl:116
func WritePreview(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, message, warning string, renderedPage string) {
//line hypview/mutators.qtpl:116
	qw422016 := qt422016.AcquireWriter(qq422016)
//line hypview/mutators.qtpl:116
	StreamPreview(qw422016, rq, hyphaName, textAreaFill, message, warning, renderedPage)
//line hypview/mutators.qtpl:116
	qt422016.ReleaseWriter(qw422016)
//line hypview/mutators.qtpl:116
}

//line hypview/mutators.qtpl:116
func Preview(rq *http.Request, hyphaName, textAreaFill, message, warning string, renderedPage string) string {
//line hypview/mutators.qtpl:116
	qb422016 := qt422016.AcquireByteBuffer()
//line hypview/mutators.qtpl:116
	WritePreview(qb422016, rq, hyphaName, textAreaFill, message, warning, renderedPage)
//line hypview/mutators.qtpl:116
	qs422016 := string(qb422016.B)
//line hypview/mutators.qtpl:116
	qt422016.ReleaseByteBuffer(qb422016)
//line hypview/mutators.qtpl:116
	return qs422016
//line hypview/mutators.qtpl:116
}

//line hypview/mutators.qtpl:118
func streameditScripts(qw422016 *qt422016.Writer) {
//line hypview/mutators.qtpl:118
	qw422016.N().S(`
<script src="/static/editor.js"></script>
`)
//line hypview/mutators.qtpl:120
	for _, scriptPath := range cfg.EditScripts {
//line hypview/mutators.qtpl:120
		qw422016.N().S(`
<script src="`)
//line hypview/mutators.qtpl:121
		qw422016.E().S(scriptPath)
//line hypview/mutators.qtpl:121
		qw422016.N().S(`"></script>
`)
//line hypview/mutators.qtpl:122
	}
//line hypview/mutators.qtpl:122
	qw422016.N().S(`
`)
//line hypview/mutators.qtpl:123
}

//line hypview/mutators.qtpl:123
func writeeditScripts(qq422016 qtio422016.Writer) {
//line hypview/mutators.qtpl:123
	qw422016 := qt422016.AcquireWriter(qq422016)
//line hypview/mutators.qtpl:123
	streameditScripts(qw422016)
//line hypview/mutators.qtpl:123
	qt422016.ReleaseWriter(qw422016)
//line hypview/mutators.qtpl:123
}

//line hypview/mutators.qtpl:123
func editScripts() string {
//line hypview/mutators.qtpl:123
	qb422016 := qt422016.AcquireByteBuffer()
//line hypview/mutators.qtpl:123
	writeeditScripts(qb422016)
//line hypview/mutators.qtpl:123
	qs422016 := string(qb422016.B)
//line hypview/mutators.qtpl:123
	qt422016.ReleaseByteBuffer(qb422016)
//line hypview/mutators.qtpl:123
	return qs422016
//line hypview/mutators.qtpl:123
}
diff --git a/hypview/view_edit.html b/hypview/view_edit.html
new file mode 100644
index 0000000..465cdf5
--- /dev/null
+++ b/hypview/view_edit.html
@@ -0,0 +1,104 @@
{{define "toolbar"}}
<aside class="edit-toolbar markup-toolbar layout-card">
    <h2 class="edit-toolbar__title layout-card__title">{{block "markup" .}}Markup{{end}}</h2>
    <section class="edit-toolbar__buttons">
        <button class="btn edit-toolbar__btn edit-toolbar__link">[[{{block "link" .}}Link{{end}}]]</button>
        <button class="btn edit-toolbar__btn edit-toolbar__titlelink">[[{{template "link" .}} | {{block "link title" .}}Title{{end}}]]</button>
        <button class="btn edit-toolbar__btn edit-toolbar__heading1">= {{block "heading" .}}Heading{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__heading2">== {{template "heading" .}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__bold"><b>**{{block "bold" .}}Bold{{end}}**</b></button>
        <button class="btn edit-toolbar__btn edit-toolbar__italic"><i>//{{block "italic" .}}Italic{{end}}//</i></button>
        <button class="btn edit-toolbar__btn edit-toolbar__highlighted"><mark>++{{block "highlight" .}}Highlight{{end}}++</mark></button>
        <button class="btn edit-toolbar__btn edit-toolbar__underline"><u>__{{block "underline" .}}Underline{{end}}__</u></button>
        <button class="btn edit-toolbar__btn edit-toolbar__monospace"><code>`{{block "mono" .}}Monospace{{end}}`</code></button>
        <button class="btn edit-toolbar__btn edit-toolbar__lifted"><sup>^^{{block "super" .}}Supertext{{end}}^^</sup></button>
        <button class="btn edit-toolbar__btn edit-toolbar__lowered"><sub>,,{{block "sub" .}}Subtext{{end}},,</sub></button>
        <button class="btn edit-toolbar__btn edit-toolbar__strikethrough"><s>~~{{block "strike" .}}Strikethrough{{end}}~~</s></button>
        <button class="btn edit-toolbar__btn edit-toolbar__rocket">=> {{block "rocket" .}}Rocketlink{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__xcl"><= {{block "transclude" .}}Transclusion{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__img"><code>img {}</code></button>
        <button class="btn edit-toolbar__btn edit-toolbar__table"><code>table {}</code></button>
        <button class="btn edit-toolbar__btn edit-toolbar__hr">{{block "hr" .}}Horizontal bar{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__codeblock">{{block "code" .}}Code block{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__bulletedlist">* {{block "bullets" .}}Bullet list{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__numberedlist">*. {{block "numbers" .}}Number list{{end}}</button>
    </section>
    <p class="edit-toolbar__help">
        {{block "mycomarkup help" .}}
            <a href="/help/en/mycomarkup" target="_blank" class="shy-link">Learn more</a> about mycomarkup
        {{end}}
    </p>
</aside>
<aside class="edit-toolbar action-toolbar layout-card">
    <h2 class="edit-toolbar__title layout-card__title">{{block "actions" .}}Actions{{end}}</h2>
    <section class="edit-toolbar__buttons">
        <button class="btn edit-toolbar__btn edit-toolbar__date">{{block "current date" .}}Insert current date{{end}}</button>
        <button class="btn edit-toolbar__btn edit-toolbar__time">{{block "current time" .}}Insert current time{{end}}</button>
        {{if .Meta.U.Group | ne "anon"}}
        <button class="btn edit-toolbar__btn edit-toolbar__user-link">{{block "selflink" .}}Link yourself{{end}}</button>
        {{end}}
    </section>
</aside>
<script src="/static/toolbar.js"></script>
{{end}}

{{define "editing hypha"}}Edit {{beautifulName .}}{{end}}
{{define "previewing hypha"}}Preview of {{beautifulName .}}{{end}}
{{define "title"}}
{{- if .Preview -}}
    {{template "previewing hypha" .HyphaName}}
{{- else -}}
    {{template "editing hypha" .HyphaName}}
{{- end -}}
{{end}}
{{define "body"}}
<main class="main-width edit {{if .Preview}}edit_with-preview{{else}}edit_no-preview{{end}}">
    <form method="post" class="edit-form" action="/upload-text/{{.HyphaName}}">
        <h1 class="edit__title">
            {{block "editing [[hypha]]" .HyphaName}}
                Edit <a href="/hypha/{{.}}">{{beautifulName .}}</a>
            {{end}}
        </h1>
        {{if .IsNew}}
            <p class="warning warning_new-hypha">
                {{block "you're creating a new hypha" .}}You are creating a new hypha.{{end}}
            </p>
        {{end}}
        <textarea name="text" class="edit-form__textarea" autofocus>{{.Content}}</textarea>
        <p class="edit-form__message-zone">
            <input
                id="text"
                type="text"
                name="message"
                class="edit-form__message"
                value="{{.Message}}"
                placeholder="{{block "describe your changes" .}}Describe your changes{{end}}"
                aria-label="{{template "describe your changes" .}}">
        </p>
        <p class="edit-form__buttons">
            <button type="submit" name="action" class="btn btn_accent edit-form__save" value="save">
                {{template "save" .}}
            </button>
            <button type="submit" name="action" class="btn edit-form__preview" value="preview">
                {{block "preview" .}}Preview{{end}}
            </button>
            <a href="/hypha/{{.HyphaName}}" class="btn btn_weak">
                {{template "cancel" .}}
            </a>
        </p>
    </form>
    {{if .Preview}}
        <p class="warning">
            {{block "preview tip" .}}Note that the hypha hasn't been saved yet. Here's the preview:{{end}}
        </p>
        <article class="edit__preview">
            {{.Preview}}
        </article>
    {{end}}
</main>
{{template "toolbar" .}}
<script src="/static/editor.js"></script>
{{range .EditScripts}}
    <script src="{{.}}"></script>
{{end}}
{{end}}
diff --git a/static/default.css b/static/default.css
index 663ca91..548b76f 100644
--- a/static/default.css
+++ b/static/default.css
@@ -99,7 +99,7 @@ textarea {font-size:16px; font-family: inherit; line-height: 150%; }
.edit-form p { margin: .25rem 0; }
.edit-form__message { width: 100%; margin: 0.25em 0; }
.edit-form__save { font-weight: bold; }
.edit-toolbar__buttons, .edit-toolbar__ad { margin: .5rem; }
.edit-toolbar__buttons, .edit-toolbar__help { margin: .5rem; }
.edit-form { height: 100%; display: flex; flex-direction: column; }

.icon {margin-right: .25rem; vertical-align: bottom; }
diff --git a/viewutil/viewutil.go b/viewutil/viewutil.go
index 3f61e50..ce29d7c 100644
--- a/viewutil/viewutil.go
+++ b/viewutil/viewutil.go
@@ -4,12 +4,13 @@ package viewutil
import (
	"embed"
	"fmt"
	"github.com/bouncepaw/mycorrhiza/cfg"
	"github.com/bouncepaw/mycorrhiza/util"
	"io/fs"
	"log"
	"strings"
	"text/template" // TODO: save the world

	"github.com/bouncepaw/mycorrhiza/cfg"
	"github.com/bouncepaw/mycorrhiza/util"
)

var (
@@ -95,6 +96,7 @@ type BaseData struct {
	HeadElements   []string
	HeaderLinks    []HeaderLink
	CommonScripts  []string
	EditScripts    []string
	Addr           string
	Title          string // TODO: remove
	Body           string // TODO: remove
@@ -119,6 +121,7 @@ func Base(meta Meta, title, body string, bodyAttributes map[string]string, headE
		HeadElements:   headElements,
		HeaderLinks:    HeaderLinks,
		CommonScripts:  cfg.CommonScripts,
		EditScripts:    cfg.EditScripts,
		Body:           body,
		BodyAttributes: bodyAttributes,
	})
diff --git a/web/mutators.go b/web/mutators.go
index e018891..89dac0c 100644
--- a/web/mutators.go
+++ b/web/mutators.go
@@ -2,12 +2,15 @@ package web

import (
	"fmt"
	"html/template"
	"log"
	"net/http"

	"github.com/bouncepaw/mycomarkup/v5"

	"github.com/bouncepaw/mycorrhiza/hypview"
	"github.com/bouncepaw/mycorrhiza/mycoopts"
	"github.com/bouncepaw/mycorrhiza/viewutil"
	"log"
	"net/http"

	"github.com/bouncepaw/mycomarkup/v5/mycocontext"

@@ -148,80 +151,65 @@ func handlerRename(w http.ResponseWriter, rq *http.Request) {
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
	util.PrepareRq(rq)
	var (
		hyphaName    = util.HyphaNameFromRq(rq, "edit")
		h            = hyphae.ByName(hyphaName)
		warning      string
		textAreaFill string
		err          error
		u            = user.FromRequest(rq)
		lc           = l18n.FromRequest(rq)
		meta         = viewutil.MetaFrom(w, rq)
		u    = user.FromRequest(rq)
		lc   = l18n.FromRequest(rq)
		meta = viewutil.MetaFrom(w, rq)

		hyphaName = util.HyphaNameFromRq(rq, "edit")
		h         = hyphae.ByName(hyphaName)

		isNew   bool
		content string
		err     error
	)

	if err := shroom.CanEdit(u, h, lc); err != nil {
		viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
		return
	}

	switch h.(type) {
	case *hyphae.EmptyHypha:
		warning = fmt.Sprintf(`<p class="warning warning_new-hypha">%s</p>`, lc.Get("edit.new_hypha"))
		isNew = true
	default:
		textAreaFill, err = hyphae.FetchMycomarkupFile(h)
		content, err = hyphae.FetchMycomarkupFile(h)
		if err != nil {
			log.Println(err)
			viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, lc.Get("ui.error_text_fetch"))
			return
		}
	}
	util.HTTP200Page(
		w,
		viewutil.Base(
			meta,
			fmt.Sprintf(lc.Get("edit.title"), util.BeautifulName(hyphaName)),
			hypview.Editor(rq, hyphaName, textAreaFill, warning),
			map[string]string{}))
	hypview.EditHypha(meta, hyphaName, isNew, content, "", "")
}

// handlerUploadText uploads a new text part for the hypha.
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
	util.PrepareRq(rq)
	var (
		u    = user.FromRequest(rq)
		meta = viewutil.MetaFrom(w, rq)

		hyphaName = util.HyphaNameFromRq(rq, "upload-text")
		h         = hyphae.ByName(hyphaName)
		textData  = rq.PostFormValue("text")
		action    = rq.PostFormValue("action")
		message   = rq.PostFormValue("message")
		u         = user.FromRequest(rq)
		lc        = l18n.FromRequest(rq)
		meta      = viewutil.MetaFrom(w, rq)
	)
		_, isNew  = h.(*hyphae.EmptyHypha)

	if action != "Preview" {
		if err := shroom.UploadText(h, []byte(textData), message, u); err != nil {
			viewutil.HttpErr(meta, http.StatusForbidden, hyphaName, err.Error())
			return
		}
	}
		textData = rq.PostFormValue("text")
		action   = rq.PostFormValue("action")
		message  = rq.PostFormValue("message")
	)

	if action == "Preview" {
	if action == "preview" {
		ctx, _ := mycocontext.ContextFromStringInput(textData, mycoopts.MarkupOptions(hyphaName))
		preview := template.HTML(mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx)))
		hypview.EditHypha(meta, hyphaName, isNew, textData, message, preview)
		return
	}

		util.HTTP200Page(
			w,
			viewutil.Base(
				meta,
				fmt.Sprintf(lc.Get("edit.preview_title"), util.BeautifulName(hyphaName)),
				hypview.Preview(
					rq,
					hyphaName,
					textData,
					message,
					"",
					mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))),
				map[string]string{},
			))
	} else {
		http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
	if err := shroom.UploadText(h, []byte(textData), message, u); err != nil {
		viewutil.HttpErr(meta, http.StatusForbidden, hyphaName, err.Error())
		return
	}
	http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
}

// handlerUploadBinary uploads a new media for the hypha.
-- 
2.32.1 (Apple Git-133)
Details
Message ID
<d4a0a168-b758-252d-b9f4-d343d96902ea@ya.ru>
In-Reply-To
<20220819192459.6863-1-umar@handlerug.me> (view parent)
DKIM signature
missing
Download raw message
Merged. Thanks! Love

> I've explored different ways of refactoring the toolbar button code, but
> they all looked far worse than the markup in the first version (weird
> template functions and map generators, or a run-time template
> generator), so I decided to leave it in. It's not actually solvable
> using the text/template syntax, which seems to be by design because
> templates should be simple and not contain code.

So be it. I trust you.
Reply to thread Export thread (mbox)