fix github button from being glitchy
This commit is contained in:
parent
f2c4574e96
commit
ce111ec03d
11 changed files with 153 additions and 31 deletions
|
|
@ -117,6 +117,14 @@ func Href(path string) Ren {
|
||||||
return Attribute("href", path)
|
return Attribute("href", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Target(target string) Ren {
|
||||||
|
return Attribute("target", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func D(value string) Ren {
|
||||||
|
return Attribute("d", value)
|
||||||
|
}
|
||||||
|
|
||||||
func Type(name string) Ren {
|
func Type(name string) Ren {
|
||||||
return Attribute("type", name)
|
return Attribute("type", name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,14 @@ func (node *Element) AppendChildren(children ...Ren) *Element {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Svg(children ...Ren) *Element {
|
||||||
|
return Tag("svg", children...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Path(children ...Ren) *Element {
|
||||||
|
return Tag("path", children...)
|
||||||
|
}
|
||||||
|
|
||||||
func TextF(format string, args ...interface{}) *TextContent {
|
func TextF(format string, args ...interface{}) *TextContent {
|
||||||
return Text(fmt.Sprintf(format, args...))
|
return Text(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
57
htmgo-site/internal/cache/simplecache.go
vendored
Normal file
57
htmgo-site/internal/cache/simplecache.go
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimpleCache struct {
|
||||||
|
data map[string]Entry
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
Value any
|
||||||
|
Expiration time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimpleCache() *SimpleCache {
|
||||||
|
return &SimpleCache{
|
||||||
|
data: make(map[string]Entry),
|
||||||
|
lock: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SimpleCache) Get(key string) (any, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
entry, ok := c.data[key]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if entry.Expiration.Before(time.Now()) {
|
||||||
|
delete(c.data, key)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return entry.Value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SimpleCache) Set(key string, value any, expiration time.Duration) {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
c.data[key] = Entry{
|
||||||
|
Value: value,
|
||||||
|
Expiration: time.Now().Add(expiration),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrSet[T any](cache *SimpleCache, key string, expiration time.Duration, cb func() (T, bool)) T {
|
||||||
|
if val, ok := cache.Get(key); ok {
|
||||||
|
return val.(T)
|
||||||
|
}
|
||||||
|
value, should := cb()
|
||||||
|
if should {
|
||||||
|
cache.Set(key, value, expiration)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
27
htmgo-site/internal/httpjson/client.go
Normal file
27
htmgo-site/internal/httpjson/client.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package httpjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get sends a GET request and decodes the response JSON into a generic type T
|
||||||
|
func Get[T any](url string) (*T, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to make GET request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("received non-OK status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result T
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
"github.com/maddalax/htmgo/framework/service"
|
"github.com/maddalax/htmgo/framework/service"
|
||||||
"htmgo-site/__htmgo"
|
"htmgo-site/__htmgo"
|
||||||
|
"htmgo-site/internal/cache"
|
||||||
"htmgo-site/internal/markdown"
|
"htmgo-site/internal/markdown"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -15,6 +16,7 @@ func main() {
|
||||||
markdownAssets := GetMarkdownAssets()
|
markdownAssets := GetMarkdownAssets()
|
||||||
|
|
||||||
service.Set(locator, service.Singleton, markdown.NewRenderer)
|
service.Set(locator, service.Singleton, markdown.NewRenderer)
|
||||||
|
service.Set(locator, service.Singleton, cache.NewSimpleCache)
|
||||||
|
|
||||||
h.Start(h.AppOpts{
|
h.Start(h.AppOpts{
|
||||||
ServiceLocator: locator,
|
ServiceLocator: locator,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
var Version = uuid.NewString()[0:6]
|
var Version = uuid.NewString()[0:6]
|
||||||
|
|
||||||
func RootPage(children ...h.Ren) *h.Element {
|
func RootPage(ctx *h.RequestContext, children ...h.Ren) *h.Element {
|
||||||
title := "htmgo"
|
title := "htmgo"
|
||||||
description := "build simple and scalable systems with go + htmx"
|
description := "build simple and scalable systems with go + htmx"
|
||||||
|
|
||||||
|
|
@ -26,9 +26,6 @@ func RootPage(children ...h.Ren) *h.Element {
|
||||||
h.Meta("og:description", description),
|
h.Meta("og:description", description),
|
||||||
h.LinkWithVersion("/public/main.css", "stylesheet", Version),
|
h.LinkWithVersion("/public/main.css", "stylesheet", Version),
|
||||||
h.ScriptWithVersion("/public/htmgo.js", Version),
|
h.ScriptWithVersion("/public/htmgo.js", Version),
|
||||||
h.Raw(`
|
|
||||||
<script src="https://buttons.github.io/buttons.js"></script>
|
|
||||||
`),
|
|
||||||
h.Style(`
|
h.Style(`
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
|
@ -37,7 +34,7 @@ func RootPage(children ...h.Ren) *h.Element {
|
||||||
),
|
),
|
||||||
h.Body(
|
h.Body(
|
||||||
h.Class("bg-stone-50 min-h-screen overflow-x-hidden"),
|
h.Class("bg-stone-50 min-h-screen overflow-x-hidden"),
|
||||||
partials.NavBar(false),
|
partials.NavBar(ctx, false),
|
||||||
h.Fragment(children...),
|
h.Fragment(children...),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ func DocsPage(ctx *h.RequestContext) *h.Page {
|
||||||
pages := dirwalk.WalkPages("md/docs", assets)
|
pages := dirwalk.WalkPages("md/docs", assets)
|
||||||
|
|
||||||
return h.NewPage(base.RootPage(
|
return h.NewPage(base.RootPage(
|
||||||
|
ctx,
|
||||||
h.Div(
|
h.Div(
|
||||||
h.Class("flex flex-col md:flex-row gap-4 justify-center mb-12"),
|
h.Class("flex flex-col md:flex-row gap-4 justify-center mb-12"),
|
||||||
partials.DocSidebar(pages),
|
partials.DocSidebar(pages),
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ var examples = []Example{
|
||||||
|
|
||||||
func ExamplesPage(ctx *h.RequestContext) *h.Page {
|
func ExamplesPage(ctx *h.RequestContext) *h.Page {
|
||||||
return h.NewPage(
|
return h.NewPage(
|
||||||
base.RootPage(h.Div(
|
base.RootPage(ctx, h.Div(
|
||||||
h.Class("flex items-center justify-center"),
|
h.Class("flex items-center justify-center"),
|
||||||
h.Div(
|
h.Div(
|
||||||
h.Class("w-full px-4 flex flex-col prose max-w-[95vw] md:max-w-3xl mt-6"),
|
h.Class("w-full px-4 flex flex-col prose max-w-[95vw] md:max-w-3xl mt-6"),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
func IndexPage(ctx *h.RequestContext) *h.Page {
|
func IndexPage(ctx *h.RequestContext) *h.Page {
|
||||||
return h.NewPage(
|
return h.NewPage(
|
||||||
base.RootPage(h.Div(
|
base.RootPage(ctx, h.Div(
|
||||||
h.Class("flex items-center justify-center "),
|
h.Class("flex items-center justify-center "),
|
||||||
h.Div(
|
h.Div(
|
||||||
h.Class("w-full px-4 flex flex-col prose md:max-w-3xl mt-6 mx-auto"),
|
h.Class("w-full px-4 flex flex-col prose md:max-w-3xl mt-6 mx-auto"),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
func CurrentTimePage(ctx *h.RequestContext) *h.Page {
|
func CurrentTimePage(ctx *h.RequestContext) *h.Page {
|
||||||
return h.NewPage(
|
return h.NewPage(
|
||||||
base.RootPage(
|
base.RootPage(
|
||||||
|
ctx,
|
||||||
h.GetPartial(
|
h.GetPartial(
|
||||||
partials.CurrentTimePartial,
|
partials.CurrentTimePartial,
|
||||||
"load, every 1s"),
|
"load, every 1s"),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ package partials
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"github.com/maddalax/htmgo/framework/service"
|
||||||
|
"htmgo-site/internal/cache"
|
||||||
|
"htmgo-site/internal/httpjson"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NavItem struct {
|
type NavItem struct {
|
||||||
|
|
@ -12,7 +16,7 @@ type NavItem struct {
|
||||||
func ToggleNavbar(ctx *h.RequestContext) *h.Partial {
|
func ToggleNavbar(ctx *h.RequestContext) *h.Partial {
|
||||||
return h.SwapManyPartial(
|
return h.SwapManyPartial(
|
||||||
ctx,
|
ctx,
|
||||||
MobileNav(h.GetQueryParam(ctx, "expanded") == "true"),
|
MobileNav(ctx, h.GetQueryParam(ctx, "expanded") == "true"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,26 +25,46 @@ var navItems = []NavItem{
|
||||||
{Name: "Examples", Url: "/examples"},
|
{Name: "Examples", Url: "/examples"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Star() *h.Element {
|
func Star(ctx *h.RequestContext) *h.Element {
|
||||||
|
|
||||||
return h.Div(
|
type Repo struct {
|
||||||
h.Script("https://buttons.github.io/buttons.js"),
|
StarCount int `json:"stargazers_count"`
|
||||||
h.Id("github-star"),
|
}
|
||||||
h.Class("min-w-[100px]"),
|
|
||||||
h.Raw(`
|
simpleCache := service.Get[cache.SimpleCache](ctx.ServiceLocator())
|
||||||
<a
|
count := cache.GetOrSet(simpleCache, "starCount", 10*time.Minute, func() (int, bool) {
|
||||||
class="github-button hidden min-w-[100px]"
|
response, err := httpjson.Get[Repo]("https://api.github.com/repos/maddalax/htmgo")
|
||||||
href="https://github.com/maddalax/htmgo"
|
if err != nil {
|
||||||
data-color-scheme="no-preference: light; light: light; dark: dark;"
|
return 0, false
|
||||||
data-icon="octicon-star"
|
}
|
||||||
data-size="large"
|
return response.StarCount, true
|
||||||
data-show-count="true"
|
})
|
||||||
aria-label="Star maddalax/htmgo on GitHub">Star</a>
|
|
||||||
`),
|
return h.A(
|
||||||
|
h.Href("https://github.com/maddalax/htmgo"),
|
||||||
|
h.Target("_blank"),
|
||||||
|
h.Class("inline-flex items-center rounded overflow-hidden shadow-sm"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex items-center px-2 py-1 bg-gray-800 text-white text-sm font-semibold hover:bg-gray-700 transition"),
|
||||||
|
h.Svg(
|
||||||
|
h.Class("w-4 h-4 -mt-0.5 mr-0.5 fill-current text-white"),
|
||||||
|
h.Attribute("xmlns", "http://www.w3.org/2000/svg"),
|
||||||
|
h.Attribute("viewBox", "0 0 24 24"),
|
||||||
|
h.Attribute("fill", "currentColor"),
|
||||||
|
h.Path(
|
||||||
|
h.D("M12 17.27l5.18 3.05-1.64-5.68 4.46-3.87-5.88-.5L12 3.5l-2.12 6.77-5.88.5 4.46 3.87-1.64 5.68L12 17.27z"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
h.Text("Star"),
|
||||||
|
),
|
||||||
|
h.If(count > 0, h.Div(
|
||||||
|
h.Class("flex items-center px-3 py-1 bg-black text-white text-sm font-semibold"),
|
||||||
|
h.Pf("%d", count),
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NavBar(expanded bool) *h.Element {
|
func NavBar(ctx *h.RequestContext, expanded bool) *h.Element {
|
||||||
prelease := h.A(h.Class("bg-yellow-200 text-yellow-800 text-center p-2 flex items-center justify-center"),
|
prelease := h.A(h.Class("bg-yellow-200 text-yellow-800 text-center p-2 flex items-center justify-center"),
|
||||||
h.Href("https://github.com/maddalax/htmgo/issues"),
|
h.Href("https://github.com/maddalax/htmgo/issues"),
|
||||||
h.Attribute("target", "_blank"),
|
h.Attribute("target", "_blank"),
|
||||||
|
|
@ -72,10 +96,7 @@ func NavBar(expanded bool) *h.Element {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
h.Div(
|
Star(ctx),
|
||||||
h.Class("ml-2 hidden md:block min-w-[99px]"),
|
|
||||||
Star(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -84,12 +105,12 @@ func NavBar(expanded bool) *h.Element {
|
||||||
return h.Div(
|
return h.Div(
|
||||||
h.Id("navbar"),
|
h.Id("navbar"),
|
||||||
prelease,
|
prelease,
|
||||||
MobileNav(expanded),
|
MobileNav(ctx, expanded),
|
||||||
desktopNav,
|
desktopNav,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MobileNav(expanded bool) *h.Element {
|
func MobileNav(ctx *h.RequestContext, expanded bool) *h.Element {
|
||||||
return h.Nav(
|
return h.Nav(
|
||||||
h.Id("mobile-nav"),
|
h.Id("mobile-nav"),
|
||||||
h.Class("block sm:hidden bg-neutral-100 border border-b-slate-300 p-4 md:p-3"),
|
h.Class("block sm:hidden bg-neutral-100 border border-b-slate-300 p-4 md:p-3"),
|
||||||
|
|
@ -107,7 +128,7 @@ func MobileNav(expanded bool) *h.Element {
|
||||||
)),
|
)),
|
||||||
h.Div(
|
h.Div(
|
||||||
h.Class("flex items-center gap-3"),
|
h.Class("flex items-center gap-3"),
|
||||||
h.Div(h.Class("mt-1"), Star()),
|
h.Div(h.Class("mt-1"), Star(ctx)),
|
||||||
h.Button(
|
h.Button(
|
||||||
h.Boost(),
|
h.Boost(),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue