Compare commits

..

2 Commits

5 changed files with 302 additions and 28 deletions

View File

@ -40,8 +40,10 @@
</div> </div>
</header> </header>
<main class="bg-base-300"> <main class="bg-base-300">
<div class="prose content-center"> <div class="w-full p-4">
{{ .Content }} <div class="prose prose-lg mx-auto">
{{ .Content }}
</div>
</div> </div>
{{ if .UseGit }} {{ if .UseGit }}
<footer class="footer px-10 py-4 border-t bg-base-200 text-base-content border-base-300"> <footer class="footer px-10 py-4 border-t bg-base-200 text-base-content border-base-300">

View File

@ -1141,6 +1141,253 @@ html {
margin-bottom: 0; margin-bottom: 0;
} }
.prose-lg {
font-size: 1.125rem;
line-height: 1.7777778;
}
.prose-lg :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
margin-bottom: 1.3333333em;
}
.prose-lg :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 1.2222222em;
line-height: 1.4545455;
margin-top: 1.0909091em;
margin-bottom: 1.0909091em;
}
.prose-lg :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.6666667em;
margin-bottom: 1.6666667em;
padding-left: 1em;
}
.prose-lg :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 2.6666667em;
margin-top: 0;
margin-bottom: 0.8333333em;
line-height: 1;
}
.prose-lg :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 1.6666667em;
margin-top: 1.8666667em;
margin-bottom: 1.0666667em;
line-height: 1.3333333;
}
.prose-lg :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 1.3333333em;
margin-top: 1.6666667em;
margin-bottom: 0.6666667em;
line-height: 1.5;
}
.prose-lg :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.7777778em;
margin-bottom: 0.4444444em;
line-height: 1.5555556;
}
.prose-lg :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.7777778em;
margin-bottom: 1.7777778em;
}
.prose-lg :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.7777778em;
margin-bottom: 1.7777778em;
}
.prose-lg :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
margin-bottom: 0;
}
.prose-lg :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.7777778em;
margin-bottom: 1.7777778em;
}
.prose-lg :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8888889em;
border-radius: 0.3125rem;
padding-top: 0.2222222em;
padding-right: 0.4444444em;
padding-bottom: 0.2222222em;
padding-left: 0.4444444em;
}
.prose-lg :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8888889em;
}
.prose-lg :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8666667em;
}
.prose-lg :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.875em;
}
.prose-lg :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8888889em;
line-height: 1.75;
margin-top: 2em;
margin-bottom: 2em;
border-radius: 0.375rem;
padding-top: 1em;
padding-right: 1.5em;
padding-bottom: 1em;
padding-left: 1.5em;
}
.prose-lg :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
margin-bottom: 1.3333333em;
padding-left: 1.5555556em;
}
.prose-lg :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
margin-bottom: 1.3333333em;
padding-left: 1.5555556em;
}
.prose-lg :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0.6666667em;
margin-bottom: 0.6666667em;
}
.prose-lg :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-left: 0.4444444em;
}
.prose-lg :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-left: 0.4444444em;
}
.prose-lg :where(.prose-lg > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0.8888889em;
margin-bottom: 0.8888889em;
}
.prose-lg :where(.prose-lg > ul > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
}
.prose-lg :where(.prose-lg > ul > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-bottom: 1.3333333em;
}
.prose-lg :where(.prose-lg > ol > li > *:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
}
.prose-lg :where(.prose-lg > ol > li > *:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-bottom: 1.3333333em;
}
.prose-lg :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0.8888889em;
margin-bottom: 0.8888889em;
}
.prose-lg :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
margin-bottom: 1.3333333em;
}
.prose-lg :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.3333333em;
}
.prose-lg :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0.6666667em;
padding-left: 1.5555556em;
}
.prose-lg :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 3.1111111em;
margin-bottom: 3.1111111em;
}
.prose-lg :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
}
.prose-lg :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
}
.prose-lg :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
}
.prose-lg :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
}
.prose-lg :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8888889em;
line-height: 1.5;
}
.prose-lg :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-right: 0.75em;
padding-bottom: 0.75em;
padding-left: 0.75em;
}
.prose-lg :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-left: 0;
}
.prose-lg :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-right: 0;
}
.prose-lg :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-top: 0.75em;
padding-right: 0.75em;
padding-bottom: 0.75em;
padding-left: 0.75em;
}
.prose-lg :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-left: 0;
}
.prose-lg :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
padding-right: 0;
}
.prose-lg :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 1.7777778em;
margin-bottom: 1.7777778em;
}
.prose-lg :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
margin-bottom: 0;
}
.prose-lg :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
font-size: 0.8888889em;
line-height: 1.5;
margin-top: 1em;
}
.prose-lg :where(.prose-lg > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-top: 0;
}
.prose-lg :where(.prose-lg > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) {
margin-bottom: 0;
}
.avatar.placeholder > div { .avatar.placeholder > div {
display: flex; display: flex;
align-items: center; align-items: center;
@ -2400,6 +2647,11 @@ html {
z-index: 1; z-index: 1;
} }
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.hidden { .hidden {
display: none; display: none;
} }
@ -2416,10 +2668,18 @@ html {
width: 1.5rem; width: 1.5rem;
} }
.w-full {
width: 100%;
}
.max-w-md { .max-w-md {
max-width: 28rem; max-width: 28rem;
} }
.max-w-none {
max-width: none;
}
.max-w-xs { .max-w-xs {
max-width: 20rem; max-width: 20rem;
} }
@ -2436,10 +2696,6 @@ html {
flex-shrink: 0; flex-shrink: 0;
} }
.content-center {
align-content: center;
}
.space-y-0 > :not([hidden]) ~ :not([hidden]) { .space-y-0 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
@ -2488,6 +2744,10 @@ html {
padding: 0.5rem; padding: 0.5rem;
} }
.p-4 {
padding: 1rem;
}
.px-1 { .px-1 {
padding-left: 0.25rem; padding-left: 0.25rem;
padding-right: 0.25rem; padding-right: 0.25rem;

View File

@ -2,3 +2,4 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@tailwindcss/typography; @tailwindcss/typography;

View File

@ -117,6 +117,8 @@ func handler(config *Config, w http.ResponseWriter, r *http.Request) {
csp := "default-src 'self'; font-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self';" csp := "default-src 'self'; font-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self';"
w.Header().Set("Content-Security-Policy", csp) w.Header().Set("Content-Security-Policy", csp)
tag := r.URL.Query().Get("tag")
markdownFiles, err := listMarkdownFiles(config.Git.LocalPath) markdownFiles, err := listMarkdownFiles(config.Git.LocalPath)
if err != nil { if err != nil {
log.Printf("Error listing markdown files: %v", err) log.Printf("Error listing markdown files: %v", err)
@ -124,7 +126,7 @@ func handler(config *Config, w http.ResponseWriter, r *http.Request) {
return return
} }
err = renderPage(w, r, config, filePath, commentsDB, markdownFiles) err = renderPage(w, r, config, filePath, commentsDB, markdownFiles, tag)
if err != nil { if err != nil {
log.Printf("Failed to render page: %v", err) log.Printf("Failed to render page: %v", err)
http.Error(w, "File not found", http.StatusNotFound) http.Error(w, "File not found", http.StatusNotFound)

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"time" "time"
"regexp"
"github.com/prologic/bitcask" "github.com/prologic/bitcask"
img64 "github.com/tenkoh/goldmark-img64" img64 "github.com/tenkoh/goldmark-img64"
@ -31,7 +32,7 @@ type Page struct {
UseGit bool UseGit bool
} }
func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath string, commentsDB *bitcask.Bitcask, pages []string) error { func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath string, commentsDB *bitcask.Bitcask, pages []string, tag string) error {
var content []byte var content []byte
var err error var err error
@ -51,7 +52,7 @@ func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath
ext := filepath.Ext(filePath) ext := filepath.Ext(filePath)
switch ext { switch ext {
case ".md": case ".md":
renderMarkdown(w, r, content, commentsDB, filePath, pages, config) renderMarkdown(w, r, content, commentsDB, filePath, pages, config, tag)
case ".html", ".css": case ".html", ".css":
renderStatic(w, content, ext) renderStatic(w, content, ext)
default: default:
@ -60,26 +61,27 @@ func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath
return nil return nil
} }
func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, commentsDB *bitcask.Bitcask, filePath string, pages []string, config *Config) { func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, commentsDB *bitcask.Bitcask, filePath string, pages []string, config *Config, tag string) {
md := goldmark.New( md := goldmark.New(
goldmark.WithExtensions( goldmark.WithExtensions(
extension.GFM, // images should probably be base64 encoded https://github.com/tenkoh/goldmark-img64 for extra performance extension.GFM, // images should probably be base64 encoded https://github.com/tenkoh/goldmark-img64 for extra performance
extension.Table, extension.Table,
highlighting.NewHighlighting( highlighting.NewHighlighting(
highlighting.WithStyle("monokai"), highlighting.WithStyle("monokai"),
), ),
img64.Img64, img64.Img64,
), // does this code below do anything useful? ), // does this code below do anything useful?
goldmark.WithParserOptions( goldmark.WithParserOptions(
parser.WithAutoHeadingID(), parser.WithAutoHeadingID(),
), ),
goldmark.WithRendererOptions( goldmark.WithRendererOptions(
html.WithXHTML(), html.WithUnsafe(), // this is a security risk but its fine for now
html.WithHardWraps(), html.WithXHTML(),
img64.WithParentPath(config.Git.LocalPath), html.WithHardWraps(),
), img64.WithParentPath(config.Git.LocalPath),
) ),
)
var author, lastModifier string var author, lastModifier string
var authoredDate, lastModifiedDate *time.Time var authoredDate, lastModifiedDate *time.Time
@ -116,8 +118,15 @@ func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, comm
return return
} }
htmlContent := mdBuf.String()
// modify <details> tag to include a tag attribute
if tag != "" {
re := regexp.MustCompile(`(?i)<details tag="` + regexp.QuoteMeta(tag) + `">`)
htmlContent = re.ReplaceAllString(htmlContent, `<details open tag="` + tag + `">`)
}
page := &Page{ page := &Page{
Content: template.HTML(mdBuf.String()), Content: template.HTML(htmlContent),
Comments: comments, Comments: comments,
Path: r.URL.Path, Path: r.URL.Path,
Author: author, Author: author,