From aae5a0e0fbf55daacdb442e757d5760fce17a9b9 Mon Sep 17 00:00:00 2001 From: csehviktor Date: Fri, 1 Aug 2025 05:52:06 +0200 Subject: [PATCH] init --- .gitignore | 1 + Makefile | 12 +++++ go.mod | 28 ++++++++++++ go.sum | 53 ++++++++++++++++++++++ handler/errors.go | 23 ++++++++++ handler/http.go | 78 ++++++++++++++++++++++++++++++++ handler/utils.go | 58 ++++++++++++++++++++++++ main.go | 33 ++++++++++++++ store/memory.go | 36 +++++++++++++++ store/memory_test.go | 44 ++++++++++++++++++ store/store.go | 7 +++ view/base.templ | 16 +++++++ view/base_templ.go | 61 +++++++++++++++++++++++++ view/bin.templ | 36 +++++++++++++++ view/bin_templ.go | 105 +++++++++++++++++++++++++++++++++++++++++++ view/style.css | 65 +++++++++++++++++++++++++++ 16 files changed, 656 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handler/errors.go create mode 100644 handler/http.go create mode 100644 handler/utils.go create mode 100644 main.go create mode 100644 store/memory.go create mode 100644 store/memory_test.go create mode 100644 store/store.go create mode 100644 view/base.templ create mode 100644 view/base_templ.go create mode 100644 view/bin.templ create mode 100644 view/bin_templ.go create mode 100644 view/style.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e2132c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +run: + @go tool templ generate + @go run . + +gen: + @go tool templ generate + +test: + @go test -v ./... + +build: + @go build -o bin/main main.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8692d48 --- /dev/null +++ b/go.mod @@ -0,0 +1,28 @@ +module github.com/csehviktor/pastebin + +go 1.24.5 + +tool github.com/a-h/templ/cmd/templ + +require ( + github.com/a-h/templ v0.3.924 + github.com/alecthomas/chroma/v2 v2.19.0 +) + +require ( + github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cli/browser v1.3.0 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/natefinch/atomic v1.0.1 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/tools v0.32.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e3a1ab0 --- /dev/null +++ b/go.sum @@ -0,0 +1,53 @@ +github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo= +github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ= +github.com/a-h/templ v0.3.924 h1:t5gZqTneXqvehpNZsgtnlOscnBboNh9aASBH2MgV/0k= +github.com/a-h/templ v0.3.924/go.mod h1:FFAu4dI//ESmEN7PQkJ7E7QfnSEMdcnu7QrAY8Dn334= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4= +github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= +github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/errors.go b/handler/errors.go new file mode 100644 index 0000000..3a278ee --- /dev/null +++ b/handler/errors.go @@ -0,0 +1,23 @@ +package handler + +import ( + "log/slog" + "net/http" +) + +func notFound(slug string, err error, w http.ResponseWriter, r *http.Request) { + respondWithError(slug, err, w, r, http.StatusNotFound) +} + +func badRequest(slug string, err error, w http.ResponseWriter, r *http.Request) { + respondWithError(slug, err, w, r, http.StatusInternalServerError) +} + +func internal(slug string, err error, w http.ResponseWriter, r *http.Request) { + respondWithError(slug, err, w, r, http.StatusInternalServerError) +} + +func respondWithError(slug string, err error, w http.ResponseWriter, r *http.Request, status int) { + slog.Error("http error occured", "slug", slug, "error", err, "path", r.URL.Path) + http.Error(w, slug, status) +} diff --git a/handler/http.go b/handler/http.go new file mode 100644 index 0000000..8aca560 --- /dev/null +++ b/handler/http.go @@ -0,0 +1,78 @@ +package handler + +import ( + "log/slog" + "net/http" + "strings" + + "github.com/a-h/templ" + "github.com/csehviktor/pastebin/store" + "github.com/csehviktor/pastebin/view" +) + +type HttpHandler struct { + store *store.MemoryStore +} + +func NewHandler(store *store.MemoryStore) *HttpHandler { + return &HttpHandler{ + store: store, + } +} + +func (h *HttpHandler) HandleSet(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + badRequest("invalid bin", err, w, r) + return + } + + content := r.FormValue("content") + + if content == "" { + badRequest("bin cant be empty", nil, w, r) + return + } + + id, err := generateId() + if err != nil { + internal("could not generate id", err, w, r) + return + } + + h.store.Set(id, content) + slog.Info("created bin", "id", id) + http.Redirect(w, r, "/"+id, http.StatusFound) +} + +func (h *HttpHandler) HandleGet(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("id") + ext := "txt" + + if index := strings.Index(id, "."); index > 0 { + if index <= len(id) { + ext = id[index+1:] + id = id[:index] + } + } + + content, exists := h.store.Get(id) + if !exists { + notFound("bin not found", nil, w, r) + return + } + + highlighted, err := highlight(content, ext, "catppuccin-macchiato") + if err != nil { + internal("could not highlight content", err, w, r) + return + } + + Render(view.BinPreviewPage(id, highlighted), w, r) +} + +func Render(component templ.Component, w http.ResponseWriter, r *http.Request) { + err := component.Render(r.Context(), w) + if err != nil { + internal("could not render template", err, w, r) + } +} diff --git a/handler/utils.go b/handler/utils.go new file mode 100644 index 0000000..f473588 --- /dev/null +++ b/handler/utils.go @@ -0,0 +1,58 @@ +package handler + +import ( + "crypto/rand" + "strings" + + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" +) + +const charset = "abcdefghijklmnopqrstuvwxyz0123456789" + +func generateId() (string, error) { + bytes := make([]byte, 10) + if _, err := rand.Read(bytes); err != nil { + return "", err + } + + for i := range bytes { + bytes[i] = charset[bytes[i]%byte(len(charset))] + } + return string(bytes), nil +} + +func highlight(content, ext, theme string) (string, error) { + lexer := lexers.Get(ext) + if lexer == nil { + lexer = lexers.Analyse(content) + if lexer == nil { + lexer = lexers.Fallback + } + } + + formatter := html.New( + html.WithLineNumbers(false), + html.Standalone(false), + html.TabWidth(4), + html.WithLineNumbers(true), + ) + + style := styles.Get(theme) + if style == nil { + style = styles.Fallback + } + + iterator, err := lexer.Tokenise(nil, content) + if err != nil { + return "", err + } + + var buf strings.Builder + err = formatter.Format(&buf, style, iterator) + if err != nil { + return "", err + } + return buf.String(), nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..1dd9939 --- /dev/null +++ b/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "log/slog" + "net/http" + + "github.com/csehviktor/pastebin/handler" + "github.com/csehviktor/pastebin/store" + "github.com/csehviktor/pastebin/view" +) + +const addr = ":3000" + +func main() { + mux := http.NewServeMux() + store := store.NewMemoryStore() + httpHandler := handler.NewHandler(store) + + mux.HandleFunc("GET /style.css", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "view/style.css") + }) + mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { + handler.Render(view.BinEditorPage(), w, r) + }) + mux.HandleFunc("POST /", httpHandler.HandleSet) + mux.HandleFunc("GET /{id}", httpHandler.HandleGet) + + slog.Info("starting http server on", "addr", addr) + err := http.ListenAndServe(addr, mux) + if err != nil { + panic(err) + } +} diff --git a/store/memory.go b/store/memory.go new file mode 100644 index 0000000..4243ecd --- /dev/null +++ b/store/memory.go @@ -0,0 +1,36 @@ +package store + +import "sync" + +type MemoryStore struct { + data map[string]string + mx sync.RWMutex +} + +func NewMemoryStore() *MemoryStore { + return &MemoryStore{ + data: make(map[string]string), + } +} + +func (s *MemoryStore) Get(key string) (string, bool) { + s.mx.RLock() + defer s.mx.RUnlock() + + content, exists := s.data[key] + return content, exists +} + +func (s *MemoryStore) Set(key string, value string) { + s.mx.Lock() + defer s.mx.Unlock() + + s.data[key] = value +} + +func (s *MemoryStore) Del(key string) { + s.mx.Lock() + defer s.mx.Unlock() + + delete(s.data, key) +} diff --git a/store/memory_test.go b/store/memory_test.go new file mode 100644 index 0000000..417d169 --- /dev/null +++ b/store/memory_test.go @@ -0,0 +1,44 @@ +package store + +import "testing" + +func TestMemoryStoreGet(t *testing.T) { + store := NewMemoryStore() + + store.Set("key", "value") + if val, _ := store.Get("key"); val != "value" { + t.Errorf("get() = %s, want %s", val, "value") + } +} + +func TestMemoryStoreExists(t *testing.T) { + store := NewMemoryStore() + + if _, exists := store.Get("something"); exists { + t.Errorf("get() = %t, want %t", exists, false) + } +} + +func TestMemoryStoreOverride(t *testing.T) { + store := NewMemoryStore() + + store.Set("key", "value") + store.Set("key", "new_value") + if val, _ := store.Get("key"); val != "new_value" { + t.Errorf("get() = %s, want %s", val, "new_value") + } +} + +func TestMemoryStoreDelete(t *testing.T) { + store := NewMemoryStore() + + store.Set("key", "value") + if _, exists := store.Get("key"); !exists { + t.Errorf("get() = %t, want %t", exists, true) + } + + store.Del("key") + if val, _ := store.Get("key"); val != "" { + t.Errorf("del() = %s, want %s", val, "") + } +} diff --git a/store/store.go b/store/store.go new file mode 100644 index 0000000..f23e2a9 --- /dev/null +++ b/store/store.go @@ -0,0 +1,7 @@ +package store + +type store interface { + Get(key string) (string, bool) + Set(key string, value string) + Del(key string) +} diff --git a/view/base.templ b/view/base.templ new file mode 100644 index 0000000..64a31cb --- /dev/null +++ b/view/base.templ @@ -0,0 +1,16 @@ +package view + +templ base(title string) { + + + + + + { title } + + + + { children... } + + +} diff --git a/view/base_templ.go b/view/base_templ.go new file mode 100644 index 0000000..9016e21 --- /dev/null +++ b/view/base_templ.go @@ -0,0 +1,61 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.924 +package view + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func base(title string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + 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 = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/base.templ`, Line: 9, Col: 17} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/view/bin.templ b/view/bin.templ new file mode 100644 index 0000000..2bb245c --- /dev/null +++ b/view/bin.templ @@ -0,0 +1,36 @@ +package view + +templ BinPreviewPage(id, content string) { + @base("bin@" + id) { + @templ.Raw(content) + } +} + +templ BinEditorPage() { + @base("bin") { +
+ + +
+ + } +} diff --git a/view/bin_templ.go b/view/bin_templ.go new file mode 100644 index 0000000..faa412b --- /dev/null +++ b/view/bin_templ.go @@ -0,0 +1,105 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.924 +package view + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func BinPreviewPage(id, content string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + 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_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templ.Raw(content).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = base("bin@"+id).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func BinEditorPage() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = base("bin").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/view/style.css b/view/style.css new file mode 100644 index 0000000..fd38fdd --- /dev/null +++ b/view/style.css @@ -0,0 +1,65 @@ +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; +} + +body { + height: 100vh; + padding: 2rem; + background: #212121; + color: #b0bec5; + line-height: 1.5; + display: flex; +} + +body, +code, +textarea { + font-family: monospace; +} + +form { + flex: 1; +} + +textarea { + height: 100%; + width: 100%; + background: none; + border: none; + outline: 0; + padding: 0; + resize: none; + overflow: auto; + color: inherit; + font-family: inherit; + font-size: 1rem; + line-height: inherit; +} + +code { + display: block; +} + +pre { + background-color: transparent !important; + height: 100%; + width: 100%; + margin: 0; + overflow: auto; + font-family: inherit; + font-size: 1rem; + line-height: inherit; +} + +button[type="submit"] { + display: none; +} + +span { + min-width: 4em; +}