diff --git a/Makefile b/Makefile
index 5f145c6..c3f15ea 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,11 @@
+# vim: noexpandtab ai sw=4 ts=4
all: build
build:
go run cmd/fetch-static/main.go
go build -o gonotes cmd/server/main.go
+
+run:
+ ./gonotes -c ./conf.toml
+
+dev: build run
diff --git a/go.mod b/go.mod
index 3993305..b383f6e 100644
--- a/go.mod
+++ b/go.mod
@@ -7,4 +7,5 @@ require github.com/yuin/goldmark v1.7.8
require (
forgejo.gwairfelin.com/max/gispatcho v0.1.2
github.com/pelletier/go-toml/v2 v2.2.3
+ github.com/teekennedy/goldmark-markdown v0.5.1
)
diff --git a/go.sum b/go.sum
index ea380da..42e8514 100644
--- a/go.sum
+++ b/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/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/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/conf/templates/view.tmpl.html b/internal/conf/templates/view.tmpl.html
index 2176027..48de3fd 100644
--- a/internal/conf/templates/view.tmpl.html
+++ b/internal/conf/templates/view.tmpl.html
@@ -7,5 +7,22 @@
Edit
Delete
-{{end}}
+
+{{end}}
diff --git a/internal/notes/notes.go b/internal/notes/notes.go
index 5791ce1..81804eb 100644
--- a/internal/notes/notes.go
+++ b/internal/notes/notes.go
@@ -12,8 +12,14 @@ import (
"path/filepath"
"forgejo.gwairfelin.com/max/gonotes/internal/conf"
+ markdown "github.com/teekennedy/goldmark-markdown"
"github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/ast"
"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
@@ -50,7 +56,7 @@ func (n *Note) Render() {
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) {
filename := filepath.Join(conf.Conf.NotesDir, fmtPath(encodedTitle))
body, err := os.ReadFile(filename)
@@ -70,6 +76,79 @@ func (n *Note) EncodedTitle() string {
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 {
title, err := base64.StdEncoding.DecodeString(encodedTitle)
if err != nil {
diff --git a/internal/notes/views/views.go b/internal/notes/views/views.go
index c5f3ea6..60e7f62 100644
--- a/internal/notes/views/views.go
+++ b/internal/notes/views/views.go
@@ -5,6 +5,7 @@ import (
"net/http"
"os"
"path/filepath"
+ "strconv"
"strings"
urls "forgejo.gwairfelin.com/max/gispatcho"
@@ -19,13 +20,14 @@ func GetRoutes(prefix string) *http.ServeMux {
myurls = urls.URLs{
Prefix: prefix,
URLs: map[string]urls.URL{
- "list": {Path: "/", Protocol: "GET", Handler: list},
- "new": {Path: "/new", Protocol: "GET", Handler: new},
- "view_": {Path: "/{note}", Protocol: "GET", Handler: view},
- "view": {Path: "/{note}/", Protocol: "GET", Handler: view},
- "delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete},
- "edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit},
- "save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save},
+ "list": {Path: "/", Protocol: "GET", Handler: list},
+ "new": {Path: "/new", Protocol: "GET", Handler: new},
+ "view_": {Path: "/{note}", Protocol: "GET", Handler: view},
+ "view": {Path: "/{note}/", Protocol: "GET", Handler: view},
+ "delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete},
+ "edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit},
+ "save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save},
+ "togglebox": {Path: "/{note}/togglebox/", Protocol: "POST", Handler: togglebox},
},
}
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)
}
-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
- Url string
+ URL string
}
func list(w http.ResponseWriter, r *http.Request) {
@@ -122,7 +143,7 @@ func list(w http.ResponseWriter, r *http.Request) {
return
}
- titlesAndUrls := make([]titleAndUrl, 0)
+ titlesAndUrls := make([]titleAndURL, 0)
for _, f := range files {
if !f.IsDir() {
@@ -133,7 +154,7 @@ func list(w http.ResponseWriter, r *http.Request) {
titlesAndUrls = append(
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)
}