From 9c5175b4954bdec2fcdf92f744601313ecb55c69 Mon Sep 17 00:00:00 2001 From: Thomas Miceli <27960254+thomiceli@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:09:23 +0100 Subject: [PATCH] Markdown preview (#224) --- internal/i18n/locales/en-US.yml | 1 + internal/render/markdown.go | 6 +++++ internal/utils/validator.go | 2 +- internal/web/gist.go | 11 ++++++++++ internal/web/server.go | 1 + public/editor.ts | 39 +++++++++++++++++++++++++++++++++ public/style.css | 4 ++++ templates/pages/create.html | 4 +++- templates/pages/edit.html | 4 +++- 9 files changed, 69 insertions(+), 3 deletions(-) diff --git a/internal/i18n/locales/en-US.yml b/internal/i18n/locales/en-US.yml index 0b03b095..a894a637 100644 --- a/internal/i18n/locales/en-US.yml +++ b/internal/i18n/locales/en-US.yml @@ -43,6 +43,7 @@ gist.new.add-file: Add file gist.new.create-public-button: Create public gist gist.new.create-unlisted-button: Create unlisted gist gist.new.create-private-button: Create private gist +gist.new.preview: Preview gist.edit.editing: Editing gist.edit.change-visibility: Make diff --git a/internal/render/markdown.go b/internal/render/markdown.go index 289cd17f..8e821ac1 100644 --- a/internal/render/markdown.go +++ b/internal/render/markdown.go @@ -41,6 +41,12 @@ func MarkdownFile(file *git.File) (RenderedFile, error) { Type: "Markdown", }, err } +func MarkdownString(content string) (string, error) { + var buf bytes.Buffer + err := newMarkdown().Convert([]byte(content), &buf) + + return buf.String(), err +} func newMarkdown() goldmark.Markdown { return goldmark.New( diff --git a/internal/utils/validator.go b/internal/utils/validator.go index d9a72c73..23aeb5fc 100644 --- a/internal/utils/validator.go +++ b/internal/utils/validator.go @@ -56,7 +56,7 @@ func validateReservedKeywords(fl validator.FieldLevel) bool { name := fl.Field().String() restrictedNames := map[string]struct{}{} - for _, restrictedName := range []string{"assets", "register", "login", "logout", "settings", "admin-panel", "all", "search", "init", "healthcheck"} { + for _, restrictedName := range []string{"assets", "register", "login", "logout", "settings", "admin-panel", "all", "search", "init", "healthcheck", "preview"} { restrictedNames[restrictedName] = struct{}{} } diff --git a/internal/web/gist.go b/internal/web/gist.go index 0d943cf3..e53d0648 100644 --- a/internal/web/gist.go +++ b/internal/web/gist.go @@ -890,3 +890,14 @@ func checkbox(ctx echo.Context) error { return plainText(ctx, 200, "ok") } + +func preview(ctx echo.Context) error { + content := ctx.FormValue("content") + + previewStr, err := render.MarkdownString(content) + if err != nil { + return errorRes(500, "Error rendering markdown", err) + } + + return plainText(ctx, 200, previewStr) +} diff --git a/internal/web/server.go b/internal/web/server.go index 8b416406..4b5c05fc 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -235,6 +235,7 @@ func NewServer(isDev bool) *Server { g1.GET("/", create, logged) g1.POST("/", processCreate, logged) + g1.GET("/preview", preview, logged) g1.GET("/healthcheck", healthcheck) diff --git a/public/editor.ts b/public/editor.ts index 269f224f..04d2814c 100644 --- a/public/editor.ts +++ b/public/editor.ts @@ -34,6 +34,45 @@ document.addEventListener("DOMContentLoaded", () => { ], }); + let mdpreview = dom.querySelector(".md-preview") as HTMLElement; + + // event if the filename ends with .md; trigger event + dom.querySelector(".form-filename")!.onkeyup = (e) => { + let filename = (e.target as HTMLInputElement).value; + if (filename.endsWith(".md")) { + mdpreview!.classList.remove("hidden"); + } else { + mdpreview!.classList.add("hidden"); + } + }; + + // @ts-ignore + const baseUrl = window.opengist_base_url || ''; + let previewShown = false; + mdpreview.onclick = () => { + previewShown = !previewShown; + let divpreview = dom.querySelector("div.preview") as HTMLElement; + let cmeditor = dom.querySelector(".cm-editor") as HTMLElement; + + if (!previewShown) { + divpreview!.classList.add("hidden"); + cmeditor!.classList.remove("hidden-important"); + return; + } else { + fetch(`${baseUrl}/preview?` + new URLSearchParams({ + content: editor.state.doc.toString() + }), { + method: 'GET', + credentials: 'same-origin', + }).then(r => r.text()).then(r => { + let divpreview = dom.querySelector("div.preview") as HTMLElement; + divpreview!.innerHTML = r; + divpreview!.classList.remove("hidden"); + cmeditor!.classList.add("hidden-important"); + }) + } + } + dom.querySelector(".editor-indent-type")!.onchange = (e) => { let newTabType = (e.target as HTMLInputElement).value; setIndentType(editor, !["tab", "space"].includes(newTabType) ? "space" : newTabType); diff --git a/public/style.css b/public/style.css index 81fae94d..85422f2c 100644 --- a/public/style.css +++ b/public/style.css @@ -174,3 +174,7 @@ dl.dl-config dd { .mermaid { background: #f6f8fa !important; } + +.hidden-important { + @apply hidden !important; +} \ No newline at end of file diff --git a/templates/pages/create.html b/templates/pages/create.html index 50af4ea1..92d34149 100644 --- a/templates/pages/create.html +++ b/templates/pages/create.html @@ -31,8 +31,9 @@

- +

+
diff --git a/templates/pages/edit.html b/templates/pages/edit.html index b1d51548..c80f7691 100644 --- a/templates/pages/edit.html +++ b/templates/pages/edit.html @@ -61,13 +61,14 @@

- +

+ {{ end }}