From 7aa3c4fb3fe32d8e55dcbde0f96132875a6681bc Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Sat, 16 Nov 2024 18:21:51 -0700 Subject: [PATCH] Get and set site name --- db.go | 71 +++++++++++++++++++++++++++++---------- db_test.go | 35 +++++++++++-------- desktop/fields/fields.go | 5 +++ desktop/main.go | 24 ++++++------- desktop/pages/HomePage.go | 59 +++++++++++++++----------------- desktop/routes/app.go | 31 +++++++++++++---- go.mod | 12 +++---- go.sum | 40 +++++++++++++++------- hx/hx.go | 54 +++++++++++++++++++++++++++++ metadata_schema.sql | 4 +++ 10 files changed, 235 insertions(+), 100 deletions(-) create mode 100644 desktop/fields/fields.go create mode 100644 hx/hx.go create mode 100644 metadata_schema.sql diff --git a/db.go b/db.go index 43bce03..4d465cc 100644 --- a/db.go +++ b/db.go @@ -1,6 +1,7 @@ package peachy import ( + _ "embed" "errors" "fmt" "log" @@ -29,8 +30,10 @@ type DB struct { conn *sqlite.Conn } -func (db *DB) Close() { - db.conn.Close() +func (db DB) Close() { + if db.conn != nil { + db.conn.Close() + } } var tableNameMatcher = match.MustCompile("(%s%s)") @@ -72,7 +75,7 @@ func setupConn(conn *sqlite.Conn) error { }) } -func Open(path string) (db *DB, err error) { +func Open(path string) (db DB, err error) { var conn *sqlite.Conn defer func() { if err != nil { @@ -84,11 +87,11 @@ func Open(path string) (db *DB, err error) { switch sqlite.ErrCode(err) { case sqlite.ResultOK: case sqlite.ResultCantOpen: - return nil, ErrFileNotExist + return db, ErrFileNotExist case sqlite.ResultNotADB: - return nil, ErrInvalidDB + return db, ErrInvalidDB default: - return nil, DBError{err} + return db, DBError{err} } var goodAppID bool @@ -98,16 +101,20 @@ func Open(path string) (db *DB, err error) { return nil }}) if !goodAppID { - return nil, ErrInvalidDB + return db, ErrInvalidDB } err = setupConn(conn) if err != nil { - return nil, err + return db, err } - return &DB{conn: conn}, nil + return DB{conn: conn}, nil } -func Create(path string) (db *DB, err error) { +//go:embed metadata_schema.sql +var metadataSchema string + +func Create(path string) (db DB, err error) { + fmt.Println("metadata schema:\n", metadataSchema) var conn *sqlite.Conn defer func() { if err != nil { @@ -117,23 +124,29 @@ func Create(path string) (db *DB, err error) { finfo, _ := os.Stat(path) if finfo != nil { - return nil, ErrFileExists + return db, ErrFileExists } conn, err = sqlite.OpenConn(path, sqlite.OpenCreate|sqlite.OpenReadWrite|sqlite.OpenWAL) if err != nil { - return nil, fmt.Errorf("could not create database: %w", err) + return db, fmt.Errorf("could not create database: %w", err) } query := fmt.Sprintf("PRAGMA application_id=%d", AppID) err = sqlitex.ExecuteTransient(conn, query, nil) if err != nil { - return nil, DBError{err} + return db, DBError{err} } + + err = sqlitex.ExecuteScript(conn, metadataSchema, nil) + if err != nil { + return db, DBError{err} + } + err = setupConn(conn) if err != nil { - return nil, err + return db, err } - return &DB{conn: conn}, nil + return DB{conn: conn}, nil } func quoteName(name string) string { @@ -161,7 +174,7 @@ const addAbstractCompositeTypeQuery = `CREATE TABLE %s ( %s_value_id INTEGER NOT NULL REFERENCES %s(id) );` -func (db *DB) AddCompositeType(name string, kind CompositeKind) error { +func (db DB) AddCompositeType(name string, kind CompositeKind) error { var kindStr string switch kind { case Record: @@ -201,7 +214,7 @@ FROM sqlite_schema WHERE parse_table_name(name, 'class') IN ('record-value', 'variant-value') ORDER BY parse_table_name(name, 'name') ASC;` -func (db *DB) GetCompositeTypes() ([]CompositeType, error) { +func (db DB) GetCompositeTypes() ([]CompositeType, error) { var results []CompositeType err := sqlitex.Execute(db.conn, getCompositeTypesQuery, &sqlitex.ExecOptions{ ResultFunc: func(stmt *sqlite.Stmt) error { @@ -220,3 +233,27 @@ func (db *DB) GetCompositeTypes() ([]CompositeType, error) { } return results, nil } + +func (db DB) SiteName() (string, error) { + var name string + err := sqlitex.Execute(db.conn, "SELECT name FROM site_metadata", &sqlitex.ExecOptions{ + ResultFunc: func(stmt *sqlite.Stmt) error { + name = stmt.ColumnText(0) + return nil + }, + }) + if err != nil { + err = fmt.Errorf("SiteName: %w", err) + } + return name, err +} + +func (db DB) SetSiteName(name string) error { + err := sqlitex.Execute(db.conn, "UPDATE site_metadata SET name = ?", &sqlitex.ExecOptions{ + Args: []any{name}, + }) + if err != nil { + return fmt.Errorf("SetSiteName: %w", err) + } + return nil +} diff --git a/db_test.go b/db_test.go index 69e2914..2bd8109 100644 --- a/db_test.go +++ b/db_test.go @@ -6,6 +6,7 @@ import ( "testing" "git.codemonkeysoftware.net/b/peachy-go" + "github.com/shoenig/test" "github.com/shoenig/test/must" "zombiezen.com/go/sqlite" ) @@ -14,12 +15,6 @@ func pending(t *testing.T) { t.Fatalf("test not implemented") } -func closeIfExists(db *peachy.DB) { - if db != nil { - db.Close() - } -} - func TestOpen(t *testing.T) { t.Run("Open succeeds on DB created by Create", func(t *testing.T) { dir := t.TempDir() @@ -29,8 +24,7 @@ func TestOpen(t *testing.T) { db.Close() db, err = peachy.Open(dbPath) - defer closeIfExists(db) - must.NotNil(t, db) + defer db.Close() must.NoError(t, err) // TODO Make sure the DB works once it has any methods. @@ -43,16 +37,14 @@ func TestOpen(t *testing.T) { conn.Close() db, err := peachy.Open(dbPath) - defer closeIfExists(db) - must.Nil(t, db) + defer db.Close() must.ErrorIs(t, err, peachy.ErrInvalidDB) }) t.Run("Open fails on nonexistent file", func(t *testing.T) { dir := t.TempDir() dbPath := filepath.Join(dir, "test.db") db, err := peachy.Open(dbPath) - defer closeIfExists(db) - must.Nil(t, db) + defer db.Close() must.ErrorIs(t, err, peachy.ErrFileNotExist) must.FileNotExists(t, dbPath) }) @@ -62,12 +54,27 @@ func TestOpen(t *testing.T) { must.NoError(t, err) db, err := peachy.Open(path) - defer closeIfExists(db) - must.Nil(t, db) + defer db.Close() must.ErrorIs(t, err, peachy.ErrInvalidDB, must.Sprint(sqlite.ErrCode(err))) }) } +func TestSiteName(t *testing.T) { + db, err := peachy.Create(":memory:") + must.NoError(t, err) + + initial, err := db.SiteName() + test.NoError(t, err) + test.EqOp(t, "", initial) + + const expected = "aoeuhtns" + test.NoError(t, db.SetSiteName(expected)) + + actual, err := db.SiteName() + test.NoError(t, err) + test.EqOp(t, expected, actual) +} + func TestCompositeTypes(t *testing.T) { db, err := peachy.Create(":memory:") must.NoError(t, err) diff --git a/desktop/fields/fields.go b/desktop/fields/fields.go new file mode 100644 index 0000000..d676138 --- /dev/null +++ b/desktop/fields/fields.go @@ -0,0 +1,5 @@ +package fields + +const ( + SiteName = "sitename" +) diff --git a/desktop/main.go b/desktop/main.go index e906b6c..29f25df 100644 --- a/desktop/main.go +++ b/desktop/main.go @@ -19,12 +19,10 @@ var assets embed.FS const ListenPort = "9245" const WindowNameMain = "mainwindow" -var globalDB *peachy.DB +var globalDB peachy.DB func closeDB() { - if globalDB != nil { - globalDB.Close() - } + globalDB.Close() } // SetURl is a workaround for WebviewWindow.SetURL not adding the base URL @@ -36,10 +34,10 @@ func SetURL(win application.Window, path string) application.Window { return win.SetURL(u) } -func setDB(db *peachy.DB) error { +func setDB(db peachy.DB) error { slog.Info("opened DB, setting URL") globalDB = db - SetURL(application.Get().GetWindowByName(WindowNameMain), "/").SetTitle("you opened a DB") + SetURL(application.Get().GetWindowByName(WindowNameMain), "/site") return nil } @@ -73,13 +71,12 @@ func dbFileFilter() []application.FileFilter { } func main() { - r := routes.NewChiRouter(ListenPort) + r := routes.NewChiRouter(ListenPort, &globalDB) - // Create the application app := application.New(application.Options{ Name: "Peachy", - Description: "A demo of using raw HTML & CSS", // Description of the application - Assets: application.AssetOptions{ // Assets to embed (our static files) + Description: "A desktop CMS for static sites", + Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), Middleware: func(next http.Handler) http.Handler { r.NotFound(next.ServeHTTP) @@ -94,16 +91,15 @@ func main() { closeDB() }) - // V3 introduces multiple windows, so we need to create a window app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Name: WindowNameMain, Title: "Your Project", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, }, - URL: "/splash", // URL to load when the window is created - Width: 1080, // Width of the window - Height: 720, // Height of the window + URL: "/", // URL to load when the window is created + Width: 1080, // Width of the window + Height: 720, // Height of the window Centered: false, }) diff --git a/desktop/pages/HomePage.go b/desktop/pages/HomePage.go index d6e08a5..91e5dd4 100644 --- a/desktop/pages/HomePage.go +++ b/desktop/pages/HomePage.go @@ -4,40 +4,37 @@ import ( h "git.codemonkeysoftware.net/b/hatmill" a "git.codemonkeysoftware.net/b/hatmill/attribute" e "git.codemonkeysoftware.net/b/hatmill/element" + "git.codemonkeysoftware.net/b/peachy-go" + "git.codemonkeysoftware.net/b/peachy-go/desktop/fields" + "git.codemonkeysoftware.net/b/peachy-go/hx" ) -func hxGet(url string) h.Attrib { - return h.Attrib{ - Key: "hx-get", - Value: a.String(url), +func HomePage(db *peachy.DB) (func() h.ParentElement, error) { + siteName, err := db.SiteName() + if err != nil { + return nil, err } -} - -func hxTrigger(event string) h.Attrib { - return h.Attrib{ - Key: "hx-trigger", - Value: a.String(event), - } -} - -func hxTarget(target string) h.Attrib { - return h.Attrib{ - Key: "hx-target", - Value: a.String(target), - } -} - -func HomePage() h.ParentElement { - return e.Div()( - e.H1()(h.Text("[untitled]")), - e.Button( - a.Type("button"), - hxGet("/hello"), - hxTrigger("click"), - hxTarget("#hello"), - )(h.Text("Click Here!")), - e.Div(a.Id("hello"))(), - ) + return func() h.ParentElement { + return e.Div()( + e.Input( + a.Type("text"), + a.Value(siteName), + a.Placeholder("site name"), + a.Name(fields.SiteName), + hx.Trigger("change"), + hx.Target("this"), + hx.Patch("/site"), + hx.Swap(hx.OuterHTML), + ), + e.Button( + a.Type("button"), + hx.Get("/hello"), + hx.Trigger("click"), + hx.Target("#hello"), + )(h.Text("Click Here!")), + e.Div(a.Id("hello"))(), + ) + }, nil } func Splash() h.Term { diff --git a/desktop/routes/app.go b/desktop/routes/app.go index 49f7b08..5ec4cd9 100644 --- a/desktop/routes/app.go +++ b/desktop/routes/app.go @@ -4,7 +4,9 @@ import ( "log/slog" "net/http" + "git.codemonkeysoftware.net/b/peachy-go" "git.codemonkeysoftware.net/b/peachy-go/desktop/components" + "git.codemonkeysoftware.net/b/peachy-go/desktop/fields" "git.codemonkeysoftware.net/b/peachy-go/desktop/pages" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -14,7 +16,7 @@ import ( /* Create a new chi router, configure it and return it. */ -func NewChiRouter(port string) *chi.Mux { +func NewChiRouter(port string, db *peachy.DB) *chi.Mux { r := chi.NewRouter() @@ -40,14 +42,31 @@ func NewChiRouter(port string) *chi.Mux { r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) - r.Get("/splash", func(w http.ResponseWriter, r *http.Request) { - slog.Info("we splashin") + r.Get("/", func(w http.ResponseWriter, r *http.Request) { HXRender(w, r, pages.Splash) }) - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - slog.Info("we homies") - HXRender(w, r, pages.HomePage) + r.Get("/site", func(w http.ResponseWriter, r *http.Request) { + page, err := pages.HomePage(db) + if err != nil { + logger.Error("internal error", "path", r.URL.Path, "error", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + HXRender(w, r, page) + }) + + r.Patch("/site", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + if r.Form.Has(fields.SiteName) { + err := db.SetSiteName(r.Form.Get(fields.SiteName)) + if err != nil { + logger.Error("internal error", "path", r.URL.Path, "error", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + } + w.WriteHeader(http.StatusOK) }) r.Get("/hello", func(w http.ResponseWriter, r *http.Request) { diff --git a/go.mod b/go.mod index 81e7e4b..c4fb9bb 100644 --- a/go.mod +++ b/go.mod @@ -9,21 +9,21 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/shoenig/test v1.11.0 pgregory.net/rapid v1.1.0 - zombiezen.com/go/sqlite v1.3.0 + zombiezen.com/go/sqlite v1.4.0 ) require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - golang.org/x/sys v0.21.0 // indirect - modernc.org/libc v1.41.0 // indirect + golang.org/x/sys v0.22.0 // indirect + modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/sqlite v1.29.1 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.33.1 // indirect ) replace git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 => ../gigaparsec diff --git a/go.sum b/go.sum index 1ddb0a1..daedcb5 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= @@ -169,8 +169,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -204,15 +204,31 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= -modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/sqlite v1.29.1 h1:19GY2qvWB4VPw0HppFlZCPAbmxFU41r+qjKZQdQ1ryA= -modernc.org/sqlite v1.29.1/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= +modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= -zombiezen.com/go/sqlite v1.3.0 h1:98g1gnCm+CNz6AuQHu0gqyw7gR2WU3O3PJufDOStpUs= -zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY= +zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU= +zombiezen.com/go/sqlite v1.4.0/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik= diff --git a/hx/hx.go b/hx/hx.go new file mode 100644 index 0000000..8654a5f --- /dev/null +++ b/hx/hx.go @@ -0,0 +1,54 @@ +package hx + +import ( + h "git.codemonkeysoftware.net/b/hatmill" + a "git.codemonkeysoftware.net/b/hatmill/attribute" +) + +func Get(url string) h.Attrib { + return h.Attrib{ + Key: "hx-get", + Value: a.String(url), + } +} + +func Patch(url string) h.Attrib { + return h.Attrib{ + Key: "hx-patch", + Value: a.String(url), + } +} + +func Trigger(event string) h.Attrib { + return h.Attrib{ + Key: "hx-trigger", + Value: a.String(event), + } +} + +func Target(target string) h.Attrib { + return h.Attrib{ + Key: "hx-target", + Value: a.String(target), + } +} + +type SwapKind string + +const ( + InnerHTML SwapKind = "innerHTML" + OuterHTML SwapKind = "outerHTML" + AfterBegin SwapKind = "afterbegin" + BeforeBegin SwapKind = "beforebegin" + BeforeEnd SwapKind = "beforeend" + AfterEnd SwapKind = "afterend" + Delete SwapKind = "delete" + None SwapKind = "none" +) + +func Swap(kind SwapKind) h.Attrib { + return h.Attrib{ + Key: "hx-target", + Value: a.String(kind), + } +} diff --git a/metadata_schema.sql b/metadata_schema.sql new file mode 100644 index 0000000..2572a67 --- /dev/null +++ b/metadata_schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE site_metadata ( + name TEXT NOT NULL +); +INSERT INTO site_metadata(name) VALUES (''); \ No newline at end of file