From a815c291074b454d4bb1a31067cd363c84df1360 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Fri, 12 Dec 2025 19:21:22 +0100 Subject: refactor(frontend): remove JS files --- README.md | 3 +-- backend/config.go | 5 ++--- backend/data.go | 6 ------ backend/home.go | 1 + backend/parser.go | 22 ++++++++++++++++++++- backend/router.go | 11 ----------- backend/section.go | 1 + backend/templates/base.html | 7 +++---- bun.lock | 21 +++++++------------- frontend/index.ts | 47 --------------------------------------------- markdown/ast_external.go | 6 +++--- markdown/eval.go | 6 +++++- package.json | 10 ++++------ 13 files changed, 48 insertions(+), 98 deletions(-) delete mode 100644 frontend/index.ts diff --git a/README.md b/README.md index cc99a04..ace50dd 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,8 @@ It aims to be simple, minimalist, brutalist, indie, and personnal. Backend written in modern Go. -Light CSS, light JS, runs everywhere. +Light CSS, runs everywhere. SSR first. -Optional HTMX to fluidify the navigation. Content written in markdown. diff --git a/backend/config.go b/backend/config.go index cd24592..44b2033 100644 --- a/backend/config.go +++ b/backend/config.go @@ -5,7 +5,6 @@ import ( "log/slog" "os" - "git.anhgelus.world/anhgelus/small-web/markdown" "github.com/pelletier/go-toml/v2" ) @@ -14,8 +13,8 @@ type Link struct { URL string `toml:"url"` } -func (l *Link) Render() template.HTML { - return markdown.RenderLink(l.Name, l.URL) +func (l *Link) Render(url string) template.HTML { + return renderLink(l.Name, l.URL, url) } type Logo struct { diff --git a/backend/data.go b/backend/data.go index 1b40e73..5bc5391 100644 --- a/backend/data.go +++ b/backend/data.go @@ -11,7 +11,6 @@ import ( "log/slog" "math/rand" "net/http" - "net/url" "regexp" "strings" txt "text/template" @@ -103,11 +102,6 @@ func (d *data) handleGeneric(w http.ResponseWriter, r *http.Request, name string panic(err) } exec := "base.html" - if r.Context().Value(isUpdateKey).(bool) { - exec = "body" - w.Header().Set("Updated-Title", url.QueryEscape(d.Title())) - w.Header().Set("Updated-Quote", url.QueryEscape(d.Quote)) - } if custom == nil { err = t.ExecuteTemplate(w, exec, d) } else { diff --git a/backend/home.go b/backend/home.go index be01b9d..f824a69 100644 --- a/backend/home.go +++ b/backend/home.go @@ -83,6 +83,7 @@ func handleGenericRoot(w http.ResponseWriter, r *http.Request, name string) { } panic(err) } + d.URL = "/" + name d.Content, ok = parse(b, new(EntryInfo), d.data) if !ok { w.WriteHeader(http.StatusInternalServerError) diff --git a/backend/parser.go b/backend/parser.go index c7d6445..b8e730b 100644 --- a/backend/parser.go +++ b/backend/parser.go @@ -18,6 +18,23 @@ type EntryInfo struct { PubLocalDate toml.LocalDate `toml:"publication_date"` } +func renderLinkFunc(url string) func(string, string) template.HTML { + return func(content, href string) template.HTML { + b := "%s`, b, href, content)) + } +} + +func renderLink(content, href, url string) template.HTML { + return renderLinkFunc(url)(content, href) +} + func parse(b []byte, info *EntryInfo, d *data) (template.HTML, bool) { var dd string splits := strings.SplitN(string(b), "---", 2) @@ -31,7 +48,10 @@ func parse(b []byte, info *EntryInfo, d *data) (template.HTML, bool) { } else { dd = string(b) } - content, err := markdown.Parse(dd, &markdown.Option{ImageSource: getStatic}) + opt := new(markdown.Option) + opt.ImageSource = getStatic + opt.RenderLink = renderLinkFunc(d.URL) + content, err := markdown.Parse(dd, opt) var errMd *markdown.ParseError errors.As(err, &errMd) if errMd != nil { diff --git a/backend/router.go b/backend/router.go index 3e6a39a..3eede92 100644 --- a/backend/router.go +++ b/backend/router.go @@ -19,7 +19,6 @@ import ( const ( Version = "0.4.0" configKey = "config" - isUpdateKey = "is_update" assetsFSKey = "assets_fs" debugKey = "debug" ) @@ -95,16 +94,6 @@ func NewRouter(debug bool, cfg *Config, assets fs.FS) *chi.Mux { next.ServeHTTP(w, r.WithContext(ctx)) }) }) - r.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - val := false - if r.Header.Get("HX-Request") == "true" { - val = true - } - ctx := context.WithValue(r.Context(), isUpdateKey, val) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - }) return r } diff --git a/backend/section.go b/backend/section.go index e6e318a..ff2cf43 100644 --- a/backend/section.go +++ b/backend/section.go @@ -216,6 +216,7 @@ func (s *Section) parse(d *sectionData, mu *sync.Mutex, path, slug string) bool panic(err) } var ok bool + d.data.URL = fmt.Sprintf("/%s/%s", s.URI, slug) d.Content, ok = parse(b, &d.EntryInfo, d.data) if !ok { return false diff --git a/backend/templates/base.html b/backend/templates/base.html index 0089587..20abcd0 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -28,10 +28,11 @@ - +
Logo - + {{ $url := .URL }} +
{{ template "body" . }} - {{ $script := asset "index.js" }} - diff --git a/bun.lock b/bun.lock index 93505bf..e8fcdfd 100644 --- a/bun.lock +++ b/bun.lock @@ -4,17 +4,16 @@ "": { "name": "small-web", "dependencies": { - "htmx.org": "2.0.7", "reset-css": "^5.0.2", - "sass": "^1.93.2", + "sass": "^1.95.1", "scss": "^0.2.4", }, "devDependencies": { "@types/bun": "latest", - "prettier": "^3.6.2", + "prettier": "^3.7.4", }, "peerDependencies": { - "typescript": "^5", + "typescript": "^5.9.3", }, }, }, @@ -47,26 +46,20 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], - "@types/bun": ["@types/bun@1.2.23", "", { "dependencies": { "bun-types": "1.2.23" } }, "sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A=="], + "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], "@types/node": ["@types/node@24.6.2", "", { "dependencies": { "undici-types": "~7.13.0" } }, "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang=="], - "@types/react": ["@types/react@19.2.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA=="], - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "bun-types": ["bun-types@1.2.23", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw=="], + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "htmx.org": ["htmx.org@2.0.7", "", {}, "sha512-YiJqF3U5KyO28VC5mPfehKJPF+n1Gni+cupK+D69TF0nm7wY6AXn3a4mPWIikfAXtl1u1F1+ZhSCS7KT8pVmqA=="], - "immutable": ["immutable@5.1.3", "", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -83,13 +76,13 @@ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "reset-css": ["reset-css@5.0.2", "", {}, "sha512-YtgUGSq5z5W0NPSjsBW7ys7rtWa8P8AiE7S6Fg3d1TQCPpAodgYyLuZYlU0AOsLtprk/fC9ormHN/0pAavVIDw=="], - "sass": ["sass@1.93.2", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg=="], + "sass": ["sass@1.95.1", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-uPoDh5NIEZV4Dp5GBodkmNY9tSQfXY02pmCcUo+FR1P+x953HGkpw+vV28D4IqYB6f8webZtwoSaZaiPtpTeMg=="], "scss": ["scss@0.2.4", "", { "dependencies": { "ometa": "0.2.2" } }, "sha512-4u8V87F+Q/upVhUmhPnB4C1R11xojkRkWjExL2v0CX2EXTg18VrKd+9JWoeyCp2VEMdSpJsyAvVU+rVjogh51A=="], diff --git a/frontend/index.ts b/frontend/index.ts deleted file mode 100644 index d3c974f..0000000 --- a/frontend/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import htmx from "htmx.org"; - -htmx.config.historyRestoreAsHxRequest = false; -htmx.config.includeIndicatorStyles = false; - -function setupAnchors() { - document.querySelectorAll("a").forEach((e) => { - // stuff related to external links are already handled in the backend - if (!e.href.startsWith(window.location.origin) && /https?:\/\//.test(e.href)) return; - // stuff related to RSS must not be processed by HTMX. - if (e.href.endsWith("/rss/") || e.href.endsWith("/rss")) { - e.target = "_blank"; - return; - } - - if (e.href == window.location.href) e.classList.add("target"); - else e.classList.remove("target"); - - if (e.hasAttribute("hx-trigger")) return; - - e.setAttribute("hx-get", e.href); - e.setAttribute("hx-trigger", "click"); - e.setAttribute("hx-target", "#content"); - e.setAttribute("hx-swap", "outerHTML show:body:top"); - htmx.process(e); - }); -} - -// updating history and window title -document.addEventListener("htmx:afterSettle", (e) => { - if (e.detail.xhr === undefined) return; - const title = e.detail.xhr.getResponseHeader("Updated-Title"); - if (title?.length !== 0) document.title = decodeURIComponent(title).replaceAll("+", " "); - const quote = e.detail.xhr.getResponseHeader("Updated-Quote"); - if (quote?.length !== 0) - document.querySelector("#quote")!.innerHTML = - "« " + decodeURIComponent(quote).replaceAll("+", " ") + " »"; - setupAnchors(); -}); - -document.body.addEventListener("htmx:beforeSwap", function (e) { - if (e.detail.xhr.status !== 404) return; - e.detail.shouldSwap = true; - e.detail.isError = false; -}); - -setupAnchors(); diff --git a/markdown/ast_external.go b/markdown/ast_external.go index ee00eab..8adeea1 100644 --- a/markdown/ast_external.go +++ b/markdown/ast_external.go @@ -6,7 +6,7 @@ import ( "regexp" ) -var externalLink = regexp.MustCompile(`https?://`) +var ExternalLink = regexp.MustCompile(`https?://`) type astLink struct { content block @@ -23,7 +23,7 @@ func (a *astLink) Eval(opt *Option) (template.HTML, *ParseError) { if err != nil { return "", err } - rr := RenderLink(string(content), string(href)) + rr := opt.RenderLink(string(content), string(href)) if a.addSpace { return " " + rr, nil } @@ -31,7 +31,7 @@ func (a *astLink) Eval(opt *Option) (template.HTML, *ParseError) { } func RenderLink(content, href string) template.HTML { - if !externalLink.Match([]byte(href)) { + if !ExternalLink.Match([]byte(href)) { return template.HTML(fmt.Sprintf(`%s`, href, content)) } return template.HTML(fmt.Sprintf(`%s`, href, content)) diff --git a/markdown/eval.go b/markdown/eval.go index 56bb989..376e577 100644 --- a/markdown/eval.go +++ b/markdown/eval.go @@ -3,7 +3,8 @@ package markdown import "html/template" type Option struct { - ImageSource func(string) string + ImageSource func(source string) string + RenderLink func(content, href string) template.HTML } func Parse(s string, opt *Option) (template.HTML, *ParseError) { @@ -18,6 +19,9 @@ func Parse(s string, opt *Option) (template.HTML, *ParseError) { if opt.ImageSource == nil { opt.ImageSource = func(s string) string { return s } } + if opt.RenderLink == nil { + opt.RenderLink = RenderLink + } return tree.Eval(opt) } diff --git a/package.json b/package.json index 6946be4..d3b2fd7 100644 --- a/package.json +++ b/package.json @@ -6,21 +6,19 @@ "scripts": { "build:sass": "sass --no-source-map --style=compressed frontend/scss/main.scss dist/styles.css", "watch:sass": "sass --watch frontend/scss/main.scss dist/styles.css", - "build:js": "bun build frontend/index.ts --outdir ./dist --minify", - "build": "bun run build:sass && bun run build:js", + "build": "bun run build:sass", "format": "prettier . --write" }, "devDependencies": { "@types/bun": "latest", - "prettier": "^3.6.2" + "prettier": "^3.7.4" }, "peerDependencies": { - "typescript": "^5" + "typescript": "^5.9.3" }, "dependencies": { - "htmx.org": "2.0.7", "reset-css": "^5.0.2", - "sass": "^1.93.2", + "sass": "^1.95.1", "scss": "^0.2.4" } } -- cgit v1.2.3