Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clickable Markdown checkboxes #182

Merged
merged 2 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ install:
build_frontend:
@echo "Building frontend assets..."
npx vite build
EMBED=1 npx postcss 'public/assets/embed-*.css' -c postcss.config.js --replace # until we can .nest { @tailwind } in Sass
@EMBED=1 npx postcss 'public/assets/embed-*.css' -c postcss.config.js --replace # until we can .nest { @tailwind } in Sass

build_backend:
@echo "Building Opengist binary..."
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/thomiceli/opengist
go 1.20

require (
github.com/Kunde21/markdownfmt/v3 v3.1.0
github.com/alecthomas/chroma/v2 v2.12.0
github.com/dustin/go-humanize v1.0.1
github.com/glebarez/go-sqlite v1.21.2
Expand Down Expand Up @@ -43,6 +44,7 @@ require (
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
Expand Down Expand Up @@ -207,6 +209,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
22 changes: 21 additions & 1 deletion internal/db/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func (gist *Gist) NbCommits() (string, error) {
}

func (gist *Gist) AddAndCommitFiles(files *[]FileDTO) error {
if err := git.CloneTmp(gist.User.Username, gist.Uuid, gist.Uuid, gist.User.Email); err != nil {
if err := git.CloneTmp(gist.User.Username, gist.Uuid, gist.Uuid, gist.User.Email, true); err != nil {
return err
}

Expand All @@ -386,6 +386,26 @@ func (gist *Gist) AddAndCommitFiles(files *[]FileDTO) error {
return git.Push(gist.Uuid)
}

func (gist *Gist) AddAndCommitFile(file *FileDTO) error {
if err := git.CloneTmp(gist.User.Username, gist.Uuid, gist.Uuid, gist.User.Email, false); err != nil {
return err
}

if err := git.SetFileContent(gist.Uuid, file.Filename, file.Content); err != nil {
return err
}

if err := git.AddAll(gist.Uuid); err != nil {
return err
}

if err := git.CommitRepository(gist.Uuid, gist.User.Username, gist.User.Email); err != nil {
return err
}

return git.Push(gist.Uuid)
}

func (gist *Gist) ForkClone(username string, uuid string) error {
return git.ForkClone(gist.User.Username, gist.Uuid, username, uuid)
}
Expand Down
12 changes: 7 additions & 5 deletions internal/git/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func GetLog(user string, gist string, skip int) ([]*Commit, error) {
return parseLog(stdout, truncateLimit), err
}

func CloneTmp(user string, gist string, gistTmpId string, email string) error {
func CloneTmp(user string, gist string, gistTmpId string, email string, remove bool) error {
repositoryPath := RepositoryPath(user, gist)

tmpPath := TmpRepositoriesPath()
Expand All @@ -219,11 +219,13 @@ func CloneTmp(user string, gist string, gistTmpId string, email string) error {
return err
}

// remove every file (and not the .git directory!)
if err = removeFilesExceptGit(tmpRepositoryPath); err != nil {
return err
// remove every file (keep the .git directory)
// useful when user wants to edit multiple files from an existing gist
if remove {
if err = removeFilesExceptGit(tmpRepositoryPath); err != nil {
return err
}
}

cmd = exec.Command("git", "config", "--local", "user.name", user)
cmd.Dir = tmpRepositoryPath
if err = cmd.Run(); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/git/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func TestInitViaGitInit(t *testing.T) {
}

func commitToBare(t *testing.T, user string, gist string, files map[string]string) {
err := CloneTmp(user, gist, gist, "[email protected]")
err := CloneTmp(user, gist, gist, "[email protected]", true)
require.NoError(t, err, "Could not commit to repository")

if len(files) > 0 {
Expand Down
66 changes: 66 additions & 0 deletions internal/render/markdown.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package render

import (
"bufio"
"bytes"
"github.com/Kunde21/markdownfmt/v3"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"github.com/yuin/goldmark"
emoji "github.com/yuin/goldmark-emoji"
highlighting "github.com/yuin/goldmark-highlighting/v2"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
astex "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"go.abhg.dev/goldmark/mermaid"
"strconv"
)

func MarkdownGistPreview(gist *db.Gist) (RenderedGist, error) {
Expand Down Expand Up @@ -43,5 +52,62 @@ func newMarkdown() goldmark.Markdown {
emoji.Emoji,
&mermaid.Extender{},
),
goldmark.WithParserOptions(
parser.WithASTTransformers(
util.Prioritized(&CheckboxTransformer{}, 10000),
),
),
)
}

type CheckboxTransformer struct{}

func (t *CheckboxTransformer) Transform(node *ast.Document, _ text.Reader, _ parser.Context) {
i := 1
err := ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if _, ok := n.(*astex.TaskCheckBox); ok {
listitem := n.Parent().Parent()
listitem.SetAttribute([]byte("data-checkbox-nb"), []byte(strconv.Itoa(i)))
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
log.Err(err)
}
}

func Checkbox(content string, checkboxNb int) (string, error) {
buf := bytes.Buffer{}
w := bufio.NewWriter(&buf)

source := []byte(content)
markdown := markdownfmt.NewGoldmark()
reader := text.NewReader(source)
document := markdown.Parser().Parse(reader)

i := 1
err := ast.Walk(document, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if listItem, ok := n.(*astex.TaskCheckBox); ok {
if i == checkboxNb {
listItem.IsChecked = !listItem.IsChecked
}
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
return "", err
}

if err = markdown.Renderer().Render(w, source, document); err != nil {
return "", err
}
_ = w.Flush()

return buf.String(), nil
}
36 changes: 36 additions & 0 deletions internal/web/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,3 +787,39 @@ func forks(ctx echo.Context) error {
setData(ctx, "revision", "HEAD")
return html(ctx, "forks.html")
}

func checkbox(ctx echo.Context) error {
filename := ctx.FormValue("file")
checkboxNb := ctx.FormValue("checkbox")

i, err := strconv.Atoi(checkboxNb)
if err != nil {
return errorRes(400, "Invalid number", nil)
}

gist := getData(ctx, "gist").(*db.Gist)
file, err := gist.File("HEAD", filename, false)
if err != nil {
return errorRes(500, "Error getting file content", err)
} else if file == nil {
return notFound("File not found")
}

markdown, err := render.Checkbox(file.Content, i)
if err != nil {
return errorRes(500, "Error checking checkbox", err)
}

if err = gist.AddAndCommitFile(&db.FileDTO{
Filename: filename,
Content: markdown,
}); err != nil {
return errorRes(500, "Error adding and committing files", err)
}

if err = gist.UpdatePreviewAndCount(); err != nil {
return errorRes(500, "Error updating the gist", err)
}

return plainText(ctx, 200, "ok")
}
1 change: 1 addition & 0 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ func NewServer(isDev bool) *Server {
g3.GET("/likes", likes)
g3.POST("/fork", fork, logged)
g3.GET("/forks", forks)
g3.PUT("/checkbox", checkbox, logged, writePermission)
}
}

Expand Down
36 changes: 36 additions & 0 deletions public/gist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,42 @@ document.querySelectorAll('.md-code-copy-btn').forEach(button => {
});
});

let checkboxes = document.querySelectorAll('li[data-checkbox-nb] input[type=checkbox]');
document.querySelectorAll<HTMLElement>('li[data-checkbox-nb]').forEach((el) => {
let input = el.firstElementChild;
(input as HTMLButtonElement).disabled = false;
let checkboxNb = (el as HTMLElement).dataset.checkboxNb;
let filename = input.parentElement.parentElement.parentElement.parentElement.parentElement.dataset.file;

input.addEventListener('change', function () {
const data = new URLSearchParams();
data.append('checkbox', checkboxNb);
data.append('file', filename);
if (document.getElementsByName('_csrf').length !== 0) {
data.append('_csrf', ((document.getElementsByName('_csrf')[0] as HTMLInputElement).value));
}
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = true;
el.classList.add('text-gray-400')
});
fetch(window.location.href.split('#')[0] + '/checkbox', {
method: 'PUT',
credentials: 'same-origin',
body: data,
}).then((response) => {
if (response.status === 200) {
checkboxes.forEach((el: HTMLButtonElement) => {
el.disabled = false;
el.classList.remove('text-gray-400')
});
}
});
})



})




3 changes: 2 additions & 1 deletion templates/pages/gist.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="grid gap-y-4">
{{ range $file := .files }}
{{ $csv := csvFile $file.File }}
<div class="rounded-md border border-1 border-gray-200 dark:border-gray-700 overflow-auto">
<div class="rounded-md border border-1 border-gray-200 dark:border-gray-700 overflow-auto" data-file="{{ $file.Filename }}">
<div class="border-b-1 border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 my-auto block">
<div class="ml-4 py-1.5 flex">

Expand Down Expand Up @@ -97,6 +97,7 @@ <h3 class="mt-2 text-sm font-medium text-slate-700 dark:text-slate-300">{{ .loca

<!-- make sure tailwind knows those classes -->
<button type="button" style="top: 1em !important; right: 1em !important;" class="hidden md-code-copy-btn absolute right-0 top-0 focus-within:z-auto rounded-md dark:border-gray-600 px-2 py-2 opacity-80 font-medium text-slate-700 bg-gray-100 dark:bg-gray-700 dark:text-slate-300 hover:bg-gray-200 dark:hover:bg-gray-600 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"><svg xmlns="https://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 7.5V6.108c0-1.135.845-2.098 1.976-2.192.373-.03.748-.057 1.123-.08M15.75 18H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08M15.75 18.75v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5A3.375 3.375 0 006.375 7.5H5.25m11.9-3.664A2.251 2.251 0 0015 2.25h-1.5a2.251 2.251 0 00-2.15 1.586m5.8 0c.065.21.1.433.1.664v.75h-6V4.5c0-.231.035-.454.1-.664M6.75 7.5H4.875c-.621 0-1.125.504-1.125 1.125v12c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V16.5a9 9 0 00-9-9z" /></svg></button>
<div class="accent-gray-400"></div>

<script type="module" src="{{ asset "gist.ts" }}"></script>

Expand Down