diff --git a/htmgo-site/pages/snippets/code.go b/htmgo-site/pages/snippets/code.go index 6a686f5..dae562a 100644 --- a/htmgo-site/pages/snippets/code.go +++ b/htmgo-site/pages/snippets/code.go @@ -1,38 +1,45 @@ package snippets import ( + "bytes" "fmt" "github.com/maddalax/htmgo/framework/h" "htmgo-site/ui" - "os" - "reflect" - "runtime" + "io" + "log/slog" + "net/http" + "time" ) -func getFunctionFilePath(fn interface{}) string { - // Get the function pointer using reflection - ptr := reflect.ValueOf(fn).Pointer() - // Get the file path and line number using runtime - fnInfo := runtime.FuncForPC(ptr) - if fnInfo == nil { - return "" - } - file, _ := fnInfo.FileLine(ptr) - return file -} - func GetGithubPath(path string) string { return fmt.Sprintf("https://github.com/maddalax/htmgo/tree/master/htmgo-site/partials%s.go", path) } -func RenderCodeToString(partial h.PartialFunc) *h.Element { - path := getFunctionFilePath(partial) - if path == "" { - return h.Empty() +func GetGithubRawPath(path string) string { + return fmt.Sprintf("https://raw.githubusercontent.com/maddalax/htmgo/master/htmgo-site/partials%s.go", path) +} + +var RenderCodeToStringCached = h.CachedPerKeyT(time.Minute*30, func(snippet *Snippet) (string, h.GetElementFunc) { + return snippet.path, func() *h.Element { + return renderCodeToString(snippet) } - bytes, err := os.ReadFile(path) +}) + +func renderCodeToString(snippet *Snippet) *h.Element { + url := GetGithubRawPath(snippet.path) + slog.Info("getting snippet source code", slog.String("url", url)) + resp, err := http.Get(url) if err != nil { return h.Empty() } - return ui.CodeSnippet(string(bytes), "border-radius: 0.5rem;") + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return h.Empty() + } + out := bytes.NewBuffer(nil) + _, err = io.Copy(out, resp.Body) + if err != nil { + return h.Empty() + } + return ui.CodeSnippet(out.String(), "border-radius: 0.5rem;") } diff --git a/htmgo-site/pages/snippets/hackernews.go b/htmgo-site/pages/snippets/hackernews.go new file mode 100644 index 0000000..d29e2fe --- /dev/null +++ b/htmgo-site/pages/snippets/hackernews.go @@ -0,0 +1,8 @@ +package snippets + +import "github.com/maddalax/htmgo/framework/h" + +func HackerNewsExample(ctx *h.RequestContext) *h.Page { + SetSnippet(ctx, &HackerNewsSnippet) + return Index(ctx) +} diff --git a/htmgo-site/pages/snippets/index.go b/htmgo-site/pages/snippets/index.go index 4f94557..a6a7830 100644 --- a/htmgo-site/pages/snippets/index.go +++ b/htmgo-site/pages/snippets/index.go @@ -36,7 +36,7 @@ func Index(ctx *h.RequestContext) *h.Page { h.IfElseLazy( snippet != nil, func() *h.Element { - return snippetView(snippet) + return snippetView(ctx, snippet) }, emptyState, ), @@ -47,7 +47,33 @@ func Index(ctx *h.RequestContext) *h.Page { ) } -func snippetView(snippet *Snippet) *h.Element { +func viewSourceButton(snippet *Snippet) *h.Element { + return h.Div( + h.Class("flex gap-2 items-center"), + h.A( + h.Fragment( + githubLogo(), + h.If( + snippet.externalRoute != "", + h.Text("View source"), + ), + ), + h.Href( + h.Ternary(snippet.sourceCodePath == "", GetGithubPath(snippet.path), snippet.sourceCodePath), + ), + h.Class("flex gap-2 items-center font-sm text-blue-500 hover:text-blue-400"), + ), + h.If( + snippet.externalRoute == "", + h.H3( + h.Text("Source Code"), + h.Class("text-lg font-bold"), + ), + ), + ) +} + +func snippetView(ctx *h.RequestContext, snippet *Snippet) *h.Element { return h.Div( h.Class("flex flex-col mx-auto items-center gap-6 max-w-[90vw] md:max-w-[75vw] xl:max-w-4xl px-8"), h.Div( @@ -63,6 +89,13 @@ func snippetView(snippet *Snippet) *h.Element { h.Class("text-slate-900"), ), ), + h.If( + snippet.externalRoute != "", + h.Div( + h.Class("mt-3"), + viewSourceButton(snippet), + ), + ), ), h.Div( h.ClassX("border px-8 py-4 rounded-md shadow-sm border-slate-200 w-full", map[string]bool{ @@ -70,44 +103,34 @@ func snippetView(snippet *Snippet) *h.Element { }), h.IfElse( snippet.externalRoute != "", - h.IFrame( - snippet.externalRoute, - h.Class("h-full min-h-[800px] w-[50vw]"), + h.Div( + h.Class("relative"), + h.IFrame( + snippet.externalRoute, + h.Class("h-full min-h-[800px] w-[50vw]"), + ), + h.A( + h.Class("absolute top-0 left-0 w-full h-full bg-transparent cursor-pointer"), + h.Href( + snippet.externalRoute, + ), + h.Target("_blank"), + ), ), h.Div( - h.Get( - h.GetPartialPath(snippet.partial), - "load", - ), + h.IfElseLazy(snippet.partial != nil, func() *h.Element { + return snippet.partial(ctx).Root + }, h.Empty), ), ), ), - h.Div( - h.Class("flex flex-col gap-2 justify-center"), + h.If( + snippet.externalRoute == "", h.Div( - h.Class("flex gap-2 items-center"), - h.A( - h.Fragment( - githubLogo(), - h.If( - snippet.externalRoute != "", - h.Text("View source"), - ), - ), - h.Href( - h.Ternary(snippet.sourceCodePath == "", GetGithubPath(snippet.path), snippet.sourceCodePath), - ), - h.Class("flex gap-1 items-center font-sm text-blue-500 hover:text-blue-400"), - ), - h.If( - snippet.externalRoute == "", - h.H3( - h.Text("Source Code"), - h.Class("text-lg font-bold"), - ), - ), + h.Class("flex flex-col gap-2 justify-center"), + viewSourceButton(snippet), + RenderCodeToStringCached(snippet), ), - h.If(snippet.externalRoute == "", RenderCodeToString(snippet.partial)), ), ) } diff --git a/htmgo-site/pages/snippets/sidebar.go b/htmgo-site/pages/snippets/sidebar.go index d303e09..0aa7cc5 100644 --- a/htmgo-site/pages/snippets/sidebar.go +++ b/htmgo-site/pages/snippets/sidebar.go @@ -17,7 +17,7 @@ func SnippetSidebar() *h.Element { ), ), h.Div( - h.Class("flex flex-col gap-4"), + h.Class("flex flex-col gap-2"), h.List(Snippets, func(entry Snippet, index int) *h.Element { return h.A( h.Href(entry.path), diff --git a/htmgo-site/pages/snippets/snippets.go b/htmgo-site/pages/snippets/snippets.go index bdda1ab..3883745 100644 --- a/htmgo-site/pages/snippets/snippets.go +++ b/htmgo-site/pages/snippets/snippets.go @@ -30,8 +30,18 @@ var ChatSnippet = Snippet{ sourceCodePath: "https://github.com/maddalax/htmgo/tree/master/examples/chat", } +var HackerNewsSnippet = Snippet{ + name: "Hacker News Clone", + description: "A hacker news reader clone built with htmgo", + sidebarName: "Hacker News Clone", + path: "/snippets/hackernews", + externalRoute: "https://hn.htmgo.dev", + sourceCodePath: "https://github.com/maddalax/htmgo/tree/master/examples/hackernews", +} + var Snippets = []Snippet{ FormWithLoadingStateSnippet, UserAuthSnippet, ChatSnippet, + HackerNewsSnippet, }