Attempt to implement checkbox shitter
This commit is contained in:
parent
0f2400435f
commit
4aa09ce502
6 changed files with 143 additions and 13 deletions
6
Makefile
6
Makefile
|
|
@ -1,5 +1,11 @@
|
||||||
|
# vim: noexpandtab ai sw=4 ts=4
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go run cmd/fetch-static/main.go
|
go run cmd/fetch-static/main.go
|
||||||
go build -o gonotes cmd/server/main.go
|
go build -o gonotes cmd/server/main.go
|
||||||
|
|
||||||
|
run:
|
||||||
|
./gonotes -c ./conf.toml
|
||||||
|
|
||||||
|
dev: build run
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -7,4 +7,5 @@ require github.com/yuin/goldmark v1.7.8
|
||||||
require (
|
require (
|
||||||
forgejo.gwairfelin.com/max/gispatcho v0.1.2
|
forgejo.gwairfelin.com/max/gispatcho v0.1.2
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3
|
github.com/pelletier/go-toml/v2 v2.2.3
|
||||||
|
github.com/teekennedy/goldmark-markdown v0.5.1
|
||||||
)
|
)
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -6,9 +6,15 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rhysd/go-fakeio v1.0.0 h1:+TjiKCOs32dONY7DaoVz/VPOdvRkPfBkEyUDIpM8FQY=
|
||||||
|
github.com/rhysd/go-fakeio v1.0.0/go.mod h1:joYxF906trVwp2JLrE4jlN7A0z6wrz8O6o1UjarbFzE=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/teekennedy/goldmark-markdown v0.5.1 h1:2lIlJ3AcIwaD1wFl4dflJSJFMhRTKEsEj+asVsu6M/0=
|
||||||
|
github.com/teekennedy/goldmark-markdown v0.5.1/go.mod h1:so260mNSPELuRyynZY18719dRYlD+OSnAovqsyrOMOM=
|
||||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
|
go.abhg.dev/goldmark/toc v0.11.0 h1:IRixVy3/yVPKvFBc37EeBPi8XLTXrtH6BYaonSjkF8o=
|
||||||
|
go.abhg.dev/goldmark/toc v0.11.0/go.mod h1:XMFIoI1Sm6dwF9vKzVDOYE/g1o5BmKXghLG8q/wJNww=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,22 @@
|
||||||
<a class="btn btn-primary" href="{{.urlEdit}}">Edit</a>
|
<a class="btn btn-primary" href="{{.urlEdit}}">Edit</a>
|
||||||
<a class="btn btn-danger" href="{{.urlDelete}}">Delete</a>
|
<a class="btn btn-danger" href="{{.urlDelete}}">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let checkBoxes = document.querySelectorAll('input[type=checkbox]')
|
||||||
|
for (const i in checkBoxes) {
|
||||||
|
let box = checkBoxes[i]
|
||||||
|
box.disabled = false
|
||||||
|
|
||||||
|
box.onchange = function(event) {
|
||||||
|
let form = new FormData()
|
||||||
|
form.append("box", i)
|
||||||
|
|
||||||
|
fetch("togglebox/", {method: "POST", body: form}).then((response) => {
|
||||||
|
location.reload();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"forgejo.gwairfelin.com/max/gonotes/internal/conf"
|
"forgejo.gwairfelin.com/max/gonotes/internal/conf"
|
||||||
|
markdown "github.com/teekennedy/goldmark-markdown"
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
|
"github.com/yuin/goldmark/ast"
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
|
astext "github.com/yuin/goldmark/extension/ast"
|
||||||
|
"github.com/yuin/goldmark/parser"
|
||||||
|
"github.com/yuin/goldmark/text"
|
||||||
|
"github.com/yuin/goldmark/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note is the central data structure. It can be Saved, Rendered and Loaded
|
// Note is the central data structure. It can be Saved, Rendered and Loaded
|
||||||
|
|
@ -50,7 +56,7 @@ func (n *Note) Render() {
|
||||||
n.BodyRendered = template.HTML(buf.String())
|
n.BodyRendered = template.HTML(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a note from the disk. The path is derived from the title
|
// LoadNote from the disk. The path is derived from the title
|
||||||
func LoadNote(encodedTitle string) (*Note, error) {
|
func LoadNote(encodedTitle string) (*Note, error) {
|
||||||
filename := filepath.Join(conf.Conf.NotesDir, fmtPath(encodedTitle))
|
filename := filepath.Join(conf.Conf.NotesDir, fmtPath(encodedTitle))
|
||||||
body, err := os.ReadFile(filename)
|
body, err := os.ReadFile(filename)
|
||||||
|
|
@ -70,6 +76,79 @@ func (n *Note) EncodedTitle() string {
|
||||||
return base64.StdEncoding.EncodeToString([]byte(n.Title))
|
return base64.StdEncoding.EncodeToString([]byte(n.Title))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type toggleCheckboxTransformer struct{ nthBox int }
|
||||||
|
|
||||||
|
func (t *toggleCheckboxTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
||||||
|
boxesFound := 0
|
||||||
|
|
||||||
|
// Walk to find checkbox, toggle checked status
|
||||||
|
ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||||
|
if !entering {
|
||||||
|
return ast.WalkContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
box, ok := n.(*astext.TaskCheckBox)
|
||||||
|
if !ok {
|
||||||
|
return ast.WalkContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if boxesFound < t.nthBox {
|
||||||
|
boxesFound += 1
|
||||||
|
return ast.WalkContinue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
box.IsChecked = !box.IsChecked
|
||||||
|
return ast.WalkStop, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *markdown.Renderer) renderTaskCheckBox(node ast.Node, entering bool) ast.WalkStatus {
|
||||||
|
if entering {
|
||||||
|
var itemPrefix []byte
|
||||||
|
l := r.rc.lists[len(r.rc.lists)-1]
|
||||||
|
|
||||||
|
if l.list.IsOrdered() {
|
||||||
|
itemPrefix = append(itemPrefix, []byte(fmt.Sprint(l.num))...)
|
||||||
|
r.rc.lists[len(r.rc.lists)-1].num += 1
|
||||||
|
}
|
||||||
|
itemPrefix = append(itemPrefix, l.list.Marker, ' ')
|
||||||
|
// Prefix the current line with the item prefix
|
||||||
|
r.rc.writer.PushPrefix(itemPrefix, 0, 0)
|
||||||
|
// Prefix subsequent lines with padding the same length as the item prefix
|
||||||
|
indentLen := int(max(r.config.NestedListLength, NestedListLengthMinimum))
|
||||||
|
indent := bytes.Repeat([]byte{' '}, indentLen)
|
||||||
|
r.rc.writer.PushPrefix(bytes.Repeat(indent, len(itemPrefix)), 1)
|
||||||
|
} else {
|
||||||
|
r.rc.writer.PopPrefix()
|
||||||
|
r.rc.writer.PopPrefix()
|
||||||
|
}
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Note) ToggleBox(nthBox int) {
|
||||||
|
log.Printf("Toggling %dth box", nthBox)
|
||||||
|
|
||||||
|
checkboxTransformer := toggleCheckboxTransformer{nthBox: nthBox}
|
||||||
|
transformer := util.Prioritized(&checkboxTransformer, 0)
|
||||||
|
|
||||||
|
renderer := markdown.NewRenderer()
|
||||||
|
renderer.Register(astext.KindTaskCheckBox, func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||||
|
})
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
md := goldmark.New(
|
||||||
|
goldmark.WithExtensions(
|
||||||
|
extension.TaskList,
|
||||||
|
),
|
||||||
|
goldmark.WithRenderer(markdown.NewRenderer()),
|
||||||
|
goldmark.WithParserOptions(parser.WithASTTransformers(transformer)),
|
||||||
|
)
|
||||||
|
|
||||||
|
md.Convert(n.Body, &buf)
|
||||||
|
n.Body = buf.Bytes()
|
||||||
|
n.Save()
|
||||||
|
}
|
||||||
|
|
||||||
func DecodeTitle(encodedTitle string) string {
|
func DecodeTitle(encodedTitle string) string {
|
||||||
title, err := base64.StdEncoding.DecodeString(encodedTitle)
|
title, err := base64.StdEncoding.DecodeString(encodedTitle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
urls "forgejo.gwairfelin.com/max/gispatcho"
|
urls "forgejo.gwairfelin.com/max/gispatcho"
|
||||||
|
|
@ -19,13 +20,14 @@ func GetRoutes(prefix string) *http.ServeMux {
|
||||||
myurls = urls.URLs{
|
myurls = urls.URLs{
|
||||||
Prefix: prefix,
|
Prefix: prefix,
|
||||||
URLs: map[string]urls.URL{
|
URLs: map[string]urls.URL{
|
||||||
"list": {Path: "/", Protocol: "GET", Handler: list},
|
"list": {Path: "/", Protocol: "GET", Handler: list},
|
||||||
"new": {Path: "/new", Protocol: "GET", Handler: new},
|
"new": {Path: "/new", Protocol: "GET", Handler: new},
|
||||||
"view_": {Path: "/{note}", Protocol: "GET", Handler: view},
|
"view_": {Path: "/{note}", Protocol: "GET", Handler: view},
|
||||||
"view": {Path: "/{note}/", Protocol: "GET", Handler: view},
|
"view": {Path: "/{note}/", Protocol: "GET", Handler: view},
|
||||||
"delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete},
|
"delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete},
|
||||||
"edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit},
|
"edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit},
|
||||||
"save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save},
|
"save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save},
|
||||||
|
"togglebox": {Path: "/{note}/togglebox/", Protocol: "POST", Handler: togglebox},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return myurls.GetRouter()
|
return myurls.GetRouter()
|
||||||
|
|
@ -109,9 +111,28 @@ func save(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.EncodedTitle()}), http.StatusFound)
|
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.EncodedTitle()}), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
type titleAndUrl struct {
|
func togglebox(w http.ResponseWriter, r *http.Request) {
|
||||||
|
title := r.PathValue("note")
|
||||||
|
nthBox, err := strconv.Atoi(r.FormValue("box"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("You fucked up boy")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
note, err := notes.LoadNote(title)
|
||||||
|
if err != nil {
|
||||||
|
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": title}), http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
note.ToggleBox(nthBox)
|
||||||
|
|
||||||
|
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.EncodedTitle()}), http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
type titleAndURL struct {
|
||||||
Title string
|
Title string
|
||||||
Url string
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func list(w http.ResponseWriter, r *http.Request) {
|
func list(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -122,7 +143,7 @@ func list(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
titlesAndUrls := make([]titleAndUrl, 0)
|
titlesAndUrls := make([]titleAndURL, 0)
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if !f.IsDir() {
|
if !f.IsDir() {
|
||||||
|
|
@ -133,7 +154,7 @@ func list(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
titlesAndUrls = append(
|
titlesAndUrls = append(
|
||||||
titlesAndUrls,
|
titlesAndUrls,
|
||||||
titleAndUrl{Title: title, Url: myurls.Reverse("view", urls.Repl{"note": encodedTitle})},
|
titleAndURL{Title: title, URL: myurls.Reverse("view", urls.Repl{"note": encodedTitle})},
|
||||||
)
|
)
|
||||||
log.Print(titlesAndUrls)
|
log.Print(titlesAndUrls)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue