Get and set site name

This commit is contained in:
Brandon Dyck 2024-11-16 18:21:51 -07:00
parent beef4523c2
commit 7aa3c4fb3f
10 changed files with 235 additions and 100 deletions

69
db.go
View File

@ -1,6 +1,7 @@
package peachy package peachy
import ( import (
_ "embed"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@ -29,8 +30,10 @@ type DB struct {
conn *sqlite.Conn conn *sqlite.Conn
} }
func (db *DB) Close() { func (db DB) Close() {
if db.conn != nil {
db.conn.Close() db.conn.Close()
}
} }
var tableNameMatcher = match.MustCompile("(%s%s)") 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 var conn *sqlite.Conn
defer func() { defer func() {
if err != nil { if err != nil {
@ -84,11 +87,11 @@ func Open(path string) (db *DB, err error) {
switch sqlite.ErrCode(err) { switch sqlite.ErrCode(err) {
case sqlite.ResultOK: case sqlite.ResultOK:
case sqlite.ResultCantOpen: case sqlite.ResultCantOpen:
return nil, ErrFileNotExist return db, ErrFileNotExist
case sqlite.ResultNotADB: case sqlite.ResultNotADB:
return nil, ErrInvalidDB return db, ErrInvalidDB
default: default:
return nil, DBError{err} return db, DBError{err}
} }
var goodAppID bool var goodAppID bool
@ -98,16 +101,20 @@ func Open(path string) (db *DB, err error) {
return nil return nil
}}) }})
if !goodAppID { if !goodAppID {
return nil, ErrInvalidDB return db, ErrInvalidDB
} }
err = setupConn(conn) err = setupConn(conn)
if err != nil { 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 var conn *sqlite.Conn
defer func() { defer func() {
if err != nil { if err != nil {
@ -117,23 +124,29 @@ func Create(path string) (db *DB, err error) {
finfo, _ := os.Stat(path) finfo, _ := os.Stat(path)
if finfo != nil { if finfo != nil {
return nil, ErrFileExists return db, ErrFileExists
} }
conn, err = sqlite.OpenConn(path, sqlite.OpenCreate|sqlite.OpenReadWrite|sqlite.OpenWAL) conn, err = sqlite.OpenConn(path, sqlite.OpenCreate|sqlite.OpenReadWrite|sqlite.OpenWAL)
if err != nil { 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) query := fmt.Sprintf("PRAGMA application_id=%d", AppID)
err = sqlitex.ExecuteTransient(conn, query, nil) err = sqlitex.ExecuteTransient(conn, query, nil)
if err != 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) err = setupConn(conn)
if err != nil { if err != nil {
return nil, err return db, err
} }
return &DB{conn: conn}, nil return DB{conn: conn}, nil
} }
func quoteName(name string) string { func quoteName(name string) string {
@ -161,7 +174,7 @@ const addAbstractCompositeTypeQuery = `CREATE TABLE %s (
%s_value_id INTEGER NOT NULL REFERENCES %s(id) %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 var kindStr string
switch kind { switch kind {
case Record: case Record:
@ -201,7 +214,7 @@ FROM sqlite_schema
WHERE parse_table_name(name, 'class') IN ('record-value', 'variant-value') WHERE parse_table_name(name, 'class') IN ('record-value', 'variant-value')
ORDER BY parse_table_name(name, 'name') ASC;` ORDER BY parse_table_name(name, 'name') ASC;`
func (db *DB) GetCompositeTypes() ([]CompositeType, error) { func (db DB) GetCompositeTypes() ([]CompositeType, error) {
var results []CompositeType var results []CompositeType
err := sqlitex.Execute(db.conn, getCompositeTypesQuery, &sqlitex.ExecOptions{ err := sqlitex.Execute(db.conn, getCompositeTypesQuery, &sqlitex.ExecOptions{
ResultFunc: func(stmt *sqlite.Stmt) error { ResultFunc: func(stmt *sqlite.Stmt) error {
@ -220,3 +233,27 @@ func (db *DB) GetCompositeTypes() ([]CompositeType, error) {
} }
return results, nil 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
}

View File

@ -6,6 +6,7 @@ import (
"testing" "testing"
"git.codemonkeysoftware.net/b/peachy-go" "git.codemonkeysoftware.net/b/peachy-go"
"github.com/shoenig/test"
"github.com/shoenig/test/must" "github.com/shoenig/test/must"
"zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite"
) )
@ -14,12 +15,6 @@ func pending(t *testing.T) {
t.Fatalf("test not implemented") t.Fatalf("test not implemented")
} }
func closeIfExists(db *peachy.DB) {
if db != nil {
db.Close()
}
}
func TestOpen(t *testing.T) { func TestOpen(t *testing.T) {
t.Run("Open succeeds on DB created by Create", func(t *testing.T) { t.Run("Open succeeds on DB created by Create", func(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
@ -29,8 +24,7 @@ func TestOpen(t *testing.T) {
db.Close() db.Close()
db, err = peachy.Open(dbPath) db, err = peachy.Open(dbPath)
defer closeIfExists(db) defer db.Close()
must.NotNil(t, db)
must.NoError(t, err) must.NoError(t, err)
// TODO Make sure the DB works once it has any methods. // TODO Make sure the DB works once it has any methods.
@ -43,16 +37,14 @@ func TestOpen(t *testing.T) {
conn.Close() conn.Close()
db, err := peachy.Open(dbPath) db, err := peachy.Open(dbPath)
defer closeIfExists(db) defer db.Close()
must.Nil(t, db)
must.ErrorIs(t, err, peachy.ErrInvalidDB) must.ErrorIs(t, err, peachy.ErrInvalidDB)
}) })
t.Run("Open fails on nonexistent file", func(t *testing.T) { t.Run("Open fails on nonexistent file", func(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
dbPath := filepath.Join(dir, "test.db") dbPath := filepath.Join(dir, "test.db")
db, err := peachy.Open(dbPath) db, err := peachy.Open(dbPath)
defer closeIfExists(db) defer db.Close()
must.Nil(t, db)
must.ErrorIs(t, err, peachy.ErrFileNotExist) must.ErrorIs(t, err, peachy.ErrFileNotExist)
must.FileNotExists(t, dbPath) must.FileNotExists(t, dbPath)
}) })
@ -62,12 +54,27 @@ func TestOpen(t *testing.T) {
must.NoError(t, err) must.NoError(t, err)
db, err := peachy.Open(path) db, err := peachy.Open(path)
defer closeIfExists(db) defer db.Close()
must.Nil(t, db)
must.ErrorIs(t, err, peachy.ErrInvalidDB, must.Sprint(sqlite.ErrCode(err))) 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) { func TestCompositeTypes(t *testing.T) {
db, err := peachy.Create(":memory:") db, err := peachy.Create(":memory:")
must.NoError(t, err) must.NoError(t, err)

5
desktop/fields/fields.go Normal file
View File

@ -0,0 +1,5 @@
package fields
const (
SiteName = "sitename"
)

View File

@ -19,12 +19,10 @@ var assets embed.FS
const ListenPort = "9245" const ListenPort = "9245"
const WindowNameMain = "mainwindow" const WindowNameMain = "mainwindow"
var globalDB *peachy.DB var globalDB peachy.DB
func closeDB() { func closeDB() {
if globalDB != nil {
globalDB.Close() globalDB.Close()
}
} }
// SetURl is a workaround for WebviewWindow.SetURL not adding the base URL // 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) return win.SetURL(u)
} }
func setDB(db *peachy.DB) error { func setDB(db peachy.DB) error {
slog.Info("opened DB, setting URL") slog.Info("opened DB, setting URL")
globalDB = db globalDB = db
SetURL(application.Get().GetWindowByName(WindowNameMain), "/").SetTitle("you opened a DB") SetURL(application.Get().GetWindowByName(WindowNameMain), "/site")
return nil return nil
} }
@ -73,13 +71,12 @@ func dbFileFilter() []application.FileFilter {
} }
func main() { func main() {
r := routes.NewChiRouter(ListenPort) r := routes.NewChiRouter(ListenPort, &globalDB)
// Create the application
app := application.New(application.Options{ app := application.New(application.Options{
Name: "Peachy", Name: "Peachy",
Description: "A demo of using raw HTML & CSS", // Description of the application Description: "A desktop CMS for static sites",
Assets: application.AssetOptions{ // Assets to embed (our static files) Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets), Handler: application.AssetFileServerFS(assets),
Middleware: func(next http.Handler) http.Handler { Middleware: func(next http.Handler) http.Handler {
r.NotFound(next.ServeHTTP) r.NotFound(next.ServeHTTP)
@ -94,14 +91,13 @@ func main() {
closeDB() closeDB()
}) })
// V3 introduces multiple windows, so we need to create a window
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Name: WindowNameMain, Name: WindowNameMain,
Title: "Your Project", Title: "Your Project",
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
}, },
URL: "/splash", // URL to load when the window is created URL: "/", // URL to load when the window is created
Width: 1080, // Width of the window Width: 1080, // Width of the window
Height: 720, // Height of the window Height: 720, // Height of the window
Centered: false, Centered: false,

View File

@ -4,40 +4,37 @@ import (
h "git.codemonkeysoftware.net/b/hatmill" h "git.codemonkeysoftware.net/b/hatmill"
a "git.codemonkeysoftware.net/b/hatmill/attribute" a "git.codemonkeysoftware.net/b/hatmill/attribute"
e "git.codemonkeysoftware.net/b/hatmill/element" 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 { func HomePage(db *peachy.DB) (func() h.ParentElement, error) {
return h.Attrib{ siteName, err := db.SiteName()
Key: "hx-get", if err != nil {
Value: a.String(url), return nil, err
} }
} return func() h.ParentElement {
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()( return e.Div()(
e.H1()(h.Text("[untitled]")), 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( e.Button(
a.Type("button"), a.Type("button"),
hxGet("/hello"), hx.Get("/hello"),
hxTrigger("click"), hx.Trigger("click"),
hxTarget("#hello"), hx.Target("#hello"),
)(h.Text("Click Here!")), )(h.Text("Click Here!")),
e.Div(a.Id("hello"))(), e.Div(a.Id("hello"))(),
) )
}, nil
} }
func Splash() h.Term { func Splash() h.Term {

View File

@ -4,7 +4,9 @@ import (
"log/slog" "log/slog"
"net/http" "net/http"
"git.codemonkeysoftware.net/b/peachy-go"
"git.codemonkeysoftware.net/b/peachy-go/desktop/components" "git.codemonkeysoftware.net/b/peachy-go/desktop/components"
"git.codemonkeysoftware.net/b/peachy-go/desktop/fields"
"git.codemonkeysoftware.net/b/peachy-go/desktop/pages" "git.codemonkeysoftware.net/b/peachy-go/desktop/pages"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
@ -14,7 +16,7 @@ import (
/* /*
Create a new chi router, configure it and return it. 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() 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.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
r.Get("/splash", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
slog.Info("we splashin")
HXRender(w, r, pages.Splash) HXRender(w, r, pages.Splash)
}) })
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/site", func(w http.ResponseWriter, r *http.Request) {
slog.Info("we homies") page, err := pages.HomePage(db)
HXRender(w, r, pages.HomePage) 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) { r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {

12
go.mod
View File

@ -9,21 +9,21 @@ require (
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/shoenig/test v1.11.0 github.com/shoenig/test v1.11.0
pgregory.net/rapid v1.1.0 pgregory.net/rapid v1.1.0
zombiezen.com/go/sqlite v1.3.0 zombiezen.com/go/sqlite v1.4.0
) )
require ( require (
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/go-cmp v0.6.0 // 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/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.22.0 // indirect
modernc.org/libc v1.41.0 // indirect modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.29.1 // indirect modernc.org/sqlite v1.33.1 // indirect
) )
replace git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 => ../gigaparsec replace git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 => ../gigaparsec

40
go.sum
View File

@ -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/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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 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= 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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= 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 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/sqlite v1.29.1 h1:19GY2qvWB4VPw0HppFlZCPAbmxFU41r+qjKZQdQ1ryA= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/sqlite v1.29.1/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0= 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 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= 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.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU=
zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY= zombiezen.com/go/sqlite v1.4.0/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=

54
hx/hx.go Normal file
View File

@ -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),
}
}

4
metadata_schema.sql Normal file
View File

@ -0,0 +1,4 @@
CREATE TABLE site_metadata (
name TEXT NOT NULL
);
INSERT INTO site_metadata(name) VALUES ('');