really liking templ

- made multiple components for reuse in templ
- going to start on actual data displaying templates
This commit is contained in:
Matthew Stobbs 2024-03-14 21:56:49 -06:00
parent db068fcecb
commit 9c35be1fcf
25 changed files with 42683 additions and 73 deletions

View File

@ -5,18 +5,18 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
pre_cmd = ["templ generate", "npx tailwindcss -i css/style.css -o public/css/style.css"]
cmd = "go build -o ./tmp/main ."
pre_cmd = [""]
cmd = "templ generate && npx tailwindcss -i css/style.css -o public/css/style.css && go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["tmp", "vendor", "testdata", "node_modules"]
exclude_dir = ["tmp", "vendor", "testdata", "node_modules", "public"]
exclude_file = []
exclude_regex = ["_test.go", ".*_templ.go"]
exclude_regex = ["_test.go", "_templ.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "css", "templ"]
include_file = ["tailwind.config.js"]
include_ext = ["go", "css", "templ", "config.js"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false

View File

@ -7,4 +7,9 @@
.notimpl {
@apply italic font-light list-image-possible;
}
div.infobox {
@apply m-2 px-4 pt-2 flex-col shadow-md rounded-2xl border-8 border-double;
@apply divide-solid divide-y-2;
}
}

8
go.mod
View File

@ -2,10 +2,8 @@ module git.staur.ca/stobbsm/clustvirt
go 1.22.1
require libvirt.org/go/libvirt v1.10001.0
require (
github.com/a-h/templ v0.2.598 // indirect
github.com/go-chi/chi/v5 v5.0.12 // indirect
gopkg.in/ffmt.v1 v1.5.6 // indirect
github.com/a-h/templ v0.2.598
github.com/go-chi/chi/v5 v5.0.12
libvirt.org/go/libvirt v1.10001.0
)

4
go.sum
View File

@ -2,7 +2,7 @@ github.com/a-h/templ v0.2.598 h1:6jMIHv6wQZvdPxTuv87erW4RqN/FPU0wk7ZHN5wVuuo=
github.com/a-h/templ v0.2.598/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
gopkg.in/ffmt.v1 v1.5.6 h1:4Bu3riZp5sAIXW2T/18JM9BkwJLodurXFR0f7PXp+cw=
gopkg.in/ffmt.v1 v1.5.6/go.mod h1:LssvGOZFiBGoBcobkTqnyh+uN1VzIRoibW+c0JI/Ha4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
libvirt.org/go/libvirt v1.10001.0 h1:lEVDNE7xfzmZXiDEGIS8NvJSuaz11OjRXw+ufbQEtPY=
libvirt.org/go/libvirt v1.10001.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=

11
main.go
View File

@ -40,16 +40,7 @@ func main() {
}
fs.ServeHTTP(w, r)
})
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if DEBUG {
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Add("Pragma", "no-cache")
w.Header().Add("Expire", "0")
}
w.Header().Add("Content", "text/html")
log.Println(view.ViewHome.Render(w, nil))
})
r.Get("/", view.HomePage)
log.Println(http.ListenAndServe(":3000", r))
}

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m 8.3076888,12.923076 2.7692302,2.769231 4.615384,-6.4615418 m 7.384614,2.7692328 a 11.076921,11.076928 0 1 1 -22.15384198,0 11.076921,11.076928 0 0 1 22.15384198,0 z"
d="m 6.9230763,14.767385 2.3076926,2.307694 3.8461551,-5.38462 m 6.153846,2.307695 a 9.2307704,9.2307764 0 1 1 -18.46154043,0 9.2307704,9.2307764 0 0 1 18.46154043,0 z"
id="path1"
style="stroke-width:1.84615;stroke:#d6d6d6;stroke-opacity:1" />
style="stroke:#d6d6d6;stroke-width:1.53846;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -29,16 +29,16 @@
inkscape:zoom="9.8333333"
inkscape:cx="9.7627119"
inkscape:cy="9.7627119"
inkscape:window-width="1312"
inkscape:window-height="449"
inkscape:window-width="1440"
inkscape:window-height="847"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m 11.076927,11.076927 0.05046,-0.02462 a 0.92307691,0.9230774 0 0 1 1.308308,1.048616 l -0.871384,3.490464 a 0.92307691,0.9230774 0 0 0 1.308308,1.049846 l 0.05046,-0.02585 m 10.153842,-4.615384 a 11.076923,11.076929 0 1 1 -22.15384598,0 11.076923,11.076929 0 0 1 22.15384598,0 z M 12.000004,7.3846176 h 0.0098 v 0.00985 h -0.0098 z"
d="m 9.2307739,13.229418 0.04205,-0.02052 a 0.76923083,0.76923136 0 0 1 1.0902571,0.873847 l -0.7261531,2.908721 a 0.76923083,0.76923136 0 0 0 1.0902561,0.874872 l 0.04205,-0.02154 m 8.461536,-3.846154 a 9.23077,9.2307764 0 1 1 -18.46153989,0 9.23077,9.2307764 0 0 1 18.46153989,0 z m -9.230765,-3.846151 h 0.0082 v 0.0082 h -0.0082 z"
id="path1"
style="stroke:#d6d6d6;stroke-width:1.84615;stroke-opacity:1" />
style="stroke:#d6d6d6;stroke-width:1.53846;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -38,7 +38,7 @@
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M 19.833912,19.783417 A 11.082911,11.052709 0 0 0 4.1603098,4.1525257 M 19.833912,19.783417 A 11.082911,11.052709 0 0 1 4.1603098,4.1525257 M 19.833912,19.783417 4.1603098,4.1525257"
d="M 16.532287,20.52382 A 9.23747,9.2323036 0 0 0 3.4685327,7.4673718 M 16.532287,20.52382 A 9.23747,9.2323036 0 0 1 3.4685327,7.4673718 M 16.532287,20.52382 3.4685327,7.4673718"
id="path1"
style="stroke:#d6d6d6;stroke-width:1.84462;stroke-opacity:1" />
style="stroke:#d6d6d6;stroke-width:1.53913;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -38,7 +38,7 @@
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M 15.692306,11.999999 H 8.3076904 m 14.7692306,0 a 11.076923,11.076929 0 1 1 -22.15384598,0 11.076923,11.076929 0 0 1 22.15384598,0 z"
d="M 13.076923,13.99792 H 6.9230762 m 12.3076928,0 a 9.23077,9.2307764 0 1 1 -18.46153955,0 9.23077,9.2307764 0 0 1 18.46153955,0 z"
id="path1"
style="stroke:#d6d6d6;stroke-width:1.84615;stroke-opacity:1" />
style="stroke:#d6d6d6;stroke-width:1.53846;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,6 +1,14 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["**/*.templ", "**/*.go"],
safelist: [
{
pattern: /(bg|text|shadow|divide|border)-(aro|blade|buffy|cullen|dracula|lincoln|marcelin|morbius|nosferatu|vanhelsing|voncount)-[0-9]+/,
},
{
pattern: /list-image-(accepted|informational|never|possible)/,
},
],
theme: {
extend: {
listStyleImage: {

View File

@ -0,0 +1,15 @@
package components
templ A(href string, text string) {
<a href={ templ.URL(href) }
class={
"after:content-link",
"after:w-3",
"after:h-3",
"after:inline-block",
"after:invert",
"text-base",
"text-morbius-100" }>
{ text }
</a>
}

View File

@ -0,0 +1,77 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func A(href string, text string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var2 = []any{
"after:content-link",
"after:w-3",
"after:h-3",
"after:inline-block",
"after:invert",
"text-base",
"text-morbius-100"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.SafeURL = templ.URL(href)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/components/anchor.templ`, Line: 12, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -0,0 +1,13 @@
package components
import "fmt"
templ ContentList(listImage string, items []string) {
<ul class={
"list-inside",
fmt.Sprintf("list-image-%s", listImage) }>
for _, li := range items {
<li>{ li }</li>
}
</ul>
}

View File

@ -0,0 +1,75 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
import "fmt"
func ContentList(listImage string, items []string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var2 = []any{
"list-inside",
fmt.Sprintf("list-image-%s", listImage)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, li := range items {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(li)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/components/contentlist.templ`, Line: 9, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -0,0 +1,5 @@
package components
templ ContentP(content string) {
<p>{ content }</p>
}

View File

@ -0,0 +1,48 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func ContentP(content string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(content)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/components/contentp.templ`, Line: 3, Col: 14}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -0,0 +1,14 @@
package components
templ FlexRow(wrap bool, comps ...templ.Component) {
<div class={
"flex",
templ.KV("flex-nowrap", !wrap),
templ.KV("flex-wrap", wrap),
templ.KV("md:flex-nowrap", wrap)
}>
for _, cmp := range comps {
@cmp
}
</div>
}

View File

@ -0,0 +1,62 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func FlexRow(wrap bool, comps ...templ.Component) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var2 = []any{
"flex",
templ.KV("flex-nowrap", !wrap),
templ.KV("flex-wrap", wrap),
templ.KV("md:flex-nowrap", wrap)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, cmp := range comps {
templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -0,0 +1,46 @@
package components
templ Index(content ...templ.Component) {
<!DOCTYPE html>
<html class={ "text-nosferatu-100" , "bg-nosferatu-900" }>
<head>
<title>Clustvirt</title>
<link href="/static/css/style.css" type="text/css" rel="stylesheet" />
</head>
<body>
<header>
@header()
</header>
<div id="content" name="content">
for _, cnt := range content {
@cnt
}
</div>
<footer>
@footer()
</footer>
</body>
</html>
}
templ header() {
<h1 class={ "text-2xl", "font-bold" }>Clustvirt</h1>
<h2 class={ "text-sm", "font-thin", "italic" }>Libvirtd simplified and clustered</h2>
<nav>
<ul>
<li>Server 1</li>
<li>Server 2</li>
</ul>
</nav>
}
templ footer() {
<div class="flex gap-4 md:gap-6 sm:gap-8 divide divide-solid">
<div id="footer_left" class="flex-auto basis-1/4 md:basis-1/3 p-2">Left</div>
<div id="footer_middle" class="flex-auto basis-1/2 md:basis-1/3 p-2">Middle</div>
<div id="footer_right" class="flex-auto basis-1/4 md:basis-1/3 p-2">Right</div>
</div>
}

View File

@ -0,0 +1,156 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func Index(content ...templ.Component) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 = []any{"text-nosferatu-100", "bg-nosferatu-900"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<html class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><head><title>Clustvirt</title><link href=\"/static/css/style.css\" type=\"text/css\" rel=\"stylesheet\"></head><body><header>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = header().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</header><div id=\"content\" name=\"content\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, cnt := range content {
templ_7745c5c3_Err = cnt.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><footer>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = footer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</footer></body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func header() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var4 = []any{"text-2xl", "font-bold"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1 class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var4).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Clustvirt</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 = []any{"text-sm", "font-thin", "italic"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2 class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var5).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Libvirtd simplified and clustered</h2><nav><ul><li>Server 1</li><li>Server 2</li></ul></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func footer() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex gap-4 md:gap-6 sm:gap-8 divide divide-solid\"><div id=\"footer_left\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Left</div><div id=\"footer_middle\" class=\"flex-auto basis-1/2 md:basis-1/3 p-2\">Middle</div><div id=\"footer_right\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Right</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -1 +1,24 @@
package components
import "fmt"
type InfoBoxConfig struct {
ColourClass string
InFlex bool
FlexBasis string
}
templ InfoBox(ibc InfoBoxConfig, title string, child templ.Component) {
<div class={
"infobox",
fmt.Sprintf("bg-%s-900", ibc.ColourClass),
fmt.Sprintf("text-%s-100", ibc.ColourClass),
fmt.Sprintf("shadow-%s-900", ibc.ColourClass),
fmt.Sprintf("divide-%s-800", ibc.ColourClass),
fmt.Sprintf("border-%s-800", ibc.ColourClass),
templ.KV(ibc.FlexBasis, ibc.InFlex),
}>
<h3 class="text-xl font-semibold">{ title }</h3>
@child
</div>
}

View File

@ -0,0 +1,85 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
import "fmt"
type InfoBoxConfig struct {
ColourClass string
InFlex bool
FlexBasis string
}
func InfoBox(ibc InfoBoxConfig, title string, child templ.Component) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var2 = []any{
"infobox",
fmt.Sprintf("bg-%s-900", ibc.ColourClass),
fmt.Sprintf("text-%s-100", ibc.ColourClass),
fmt.Sprintf("shadow-%s-900", ibc.ColourClass),
fmt.Sprintf("divide-%s-800", ibc.ColourClass),
fmt.Sprintf("border-%s-800", ibc.ColourClass),
templ.KV(ibc.FlexBasis, ibc.InFlex),
}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><h3 class=\"text-xl font-semibold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/components/infobox.templ`, Line: 20, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h3>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = child.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

8
view/components/util.go Normal file
View File

@ -0,0 +1,8 @@
package components
import "fmt"
func NoWrapSize(size string) string {
return fmt.Sprintf("%s:flex-nowrap", size)
}

162
view/templHome.go Normal file
View File

@ -0,0 +1,162 @@
package view
import (
"context"
"net/http"
"git.staur.ca/stobbsm/clustvirt/view/components"
)
// HomePage is the homepage, generated via templ components
func HomePage(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
ibWhatIsThis := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "aro",
InFlex: true,
FlexBasis: "basis-1/3",
},
"What is this?",
components.ContentP(
`Clustvirt (work in progress name) aims to be the agnostic
cluster controller for libvirtd. The server component is
used to display both the WebUI and run the REST API used to
control one to many libvirtd hosts to manage virual machines,
LXC containers (through libvirtd), gather information about
each host, and monitor each host.`,
),
)
ibWhy := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "dracula",
InFlex: true,
FlexBasis: "basis-2/3",
},
"Why?",
components.ContentList(
"informational",
[]string{
"Broadcom buying VMWare, and VMWare losing a free teir for homelabbers pissed me off",
"Vendor lock-in pisses me off",
"Even good open source Hyperconverged systems (Proxmox, as an example) exhibit a form of vendor lock-in",
"Libvirt is terrific, has the functionality for everything those other providers do, but there really is not a great option for those dipping their toes into Open Source",
"Its fun to build things that solve a need",
"I really want to do it",
},
),
)
ibProjectGoals := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "blade",
InFlex: true,
FlexBasis: "basis-7/12",
},
"Project Goals",
components.ContentList(
"accepted",
[]string{
"Open source, currently on the MIT license",
"Base OS Agnostic. If it can run libvirtd, this should be able to control it on some level",
"Control the Virtual Machine life cycle on one or more libvirtd hosts",
"Add clusting capabilities to libvirtd host, including;",
"Migration of VMs",
"Syncronizing secrets",
"Syncronizing VLANs, bridges, host only networking",
"Sharing HA storage availability",
"Locking shared resources like disks",
"Starting VMs marked for HA on another host when one goes down",
"Manage a library of Cloud-init resources and templates to build new VMs quickly",
"Local Storage management, including local directory, lvm, zfs (if installed)",
"Advanced Storage management, such as Ceph, glusterfs, drbd, iscsi, nfs",
"Storage syncronization of local disks between hosts (zfs snapshots, lvm snapshots, rsync)",
"Backup scheduling, creation, restoration",
},
),
)
ibStretchGoals := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "cullen",
InFlex: true,
FlexBasis: "basis-5/12",
},
"Stretch Goals",
components.ContentList(
"possible",
[]string{
"Install the OS which libvirtd is running on",
"Install/provision libvirtd on a host that does not have it installed",
"Tools to move from one vendor to clustvirt/libvirtd",
"VM templates for common aspects of VM creation and management, like appliances",
"External tool access that can be used to manage things that are not managed here (cephadm dashboard, for instance)",
},
),
)
ibRedditRequested := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "morbius",
InFlex: true,
FlexBasis: "basis-8/12",
},
"Reddit Requested Features",
components.ContentList(
"possible",
[]string{
"Search/Filter on hosts/vms - @Lopsided_Speaker_553",
"Balance on resource usage per host/Automattically migrate to least used host - @Lopsided_Speaker_553",
"Support inter-vm only commmunication (VxLAN style) - @Lopsided_Speaker_553",
"Deploy VMs using only API - @Lopsided_Speaker_553",
"Well documented, first class API - @kasperlitheater",
"Bootstrap service to configure a new server - @phatpappa_",
"For the love of kitten, don't use XML as configuration files - @pascalbrax",
"Expose the Cluster Manager functionalities as API - @raven2611",
"CPU architecture awareness for migrations - @raven2611",
"Inter VM Communications via VXLAN/EVPN - @raven2611",
},
),
)
ibNeverHappening := components.InfoBox(
components.InfoBoxConfig{
ColourClass: "marcelin",
InFlex: true,
FlexBasis: "basis-4/12",
},
"Never Going to Happen",
components.ContentList(
"never",
[]string{
"Kubernetes",
"Application container management (docker, podman, etc)",
"Become an OS",
"Have a paywall",
"Vendor lock-in",
"Become a commercial entity (even indirectly)",
"Anything that does not have an Open Source standard behind it",
"Directly control a guest Operating System",
},
),
)
idx := components.Index(
components.FlexRow(
true,
ibWhatIsThis,
ibWhy,
),
components.FlexRow(
true,
ibProjectGoals,
ibStretchGoals,
),
components.FlexRow(
true,
ibRedditRequested,
ibNeverHappening,
),
)
idx.Render(context.Background(), w)
}