Compare commits
2 Commits
0bab92db02
...
7aa3c4fb3f
Author | SHA1 | Date | |
---|---|---|---|
7aa3c4fb3f | |||
beef4523c2 |
69
db.go
69
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() {
|
||||
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
|
||||
}
|
||||
|
35
db_test.go
35
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)
|
||||
|
@ -1,3 +0,0 @@
|
||||
package app
|
||||
|
||||
// TODO: Add your code here
|
5
desktop/fields/fields.go
Normal file
5
desktop/fields/fields.go
Normal file
@ -0,0 +1,5 @@
|
||||
package fields
|
||||
|
||||
const (
|
||||
SiteName = "sitename"
|
||||
)
|
@ -2,9 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"git.codemonkeysoftware.net/b/peachy-go"
|
||||
"git.codemonkeysoftware.net/b/peachy-go/desktop/routes"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
@ -12,15 +16,67 @@ import (
|
||||
//go:embed all:static
|
||||
var assets embed.FS
|
||||
|
||||
const ListenPort = "9245"
|
||||
const WindowNameMain = "mainwindow"
|
||||
|
||||
var globalDB peachy.DB
|
||||
|
||||
func closeDB() {
|
||||
globalDB.Close()
|
||||
}
|
||||
|
||||
// SetURl is a workaround for WebviewWindow.SetURL not adding the base URL
|
||||
// to relative URLs.
|
||||
// TODO Report this or something.
|
||||
func SetURL(win application.Window, path string) application.Window {
|
||||
slog.Info("setting URL the hard way")
|
||||
u, _ := url.JoinPath("http://wails.localhost:"+ListenPort+"/", path)
|
||||
return win.SetURL(u)
|
||||
}
|
||||
|
||||
func setDB(db peachy.DB) error {
|
||||
slog.Info("opened DB, setting URL")
|
||||
globalDB = db
|
||||
SetURL(application.Get().GetWindowByName(WindowNameMain), "/site")
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDB(filename string) error {
|
||||
db, err := peachy.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create database: %w", err)
|
||||
}
|
||||
return setDB(db)
|
||||
}
|
||||
|
||||
func openDB(filename string) error {
|
||||
db, err := peachy.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open database: %w", err)
|
||||
}
|
||||
return setDB(db)
|
||||
}
|
||||
|
||||
func showError(err error) {
|
||||
if err != nil {
|
||||
application.ErrorDialog().SetMessage(err.Error()).Show()
|
||||
}
|
||||
}
|
||||
|
||||
func dbFileFilter() []application.FileFilter {
|
||||
return []application.FileFilter{
|
||||
{DisplayName: "Peachy database", Pattern: "*.pit"},
|
||||
{DisplayName: "All Files", Pattern: "*.*"},
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := routes.NewChiRouter(ListenPort, &globalDB)
|
||||
|
||||
r := routes.NewChiRouter()
|
||||
|
||||
// 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)
|
||||
@ -31,9 +87,12 @@ func main() {
|
||||
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||
},
|
||||
})
|
||||
app.OnShutdown(func() {
|
||||
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,
|
||||
@ -52,31 +111,24 @@ func main() {
|
||||
menu.FindByRole(application.NewFile).OnClick(func(ctx *application.Context) {
|
||||
d := application.SaveFileDialog()
|
||||
d.SetOptions(&application.SaveFileDialogOptions{
|
||||
Filters: []application.FileFilter{
|
||||
{DisplayName: "Peachy database", Pattern: "*.pit"},
|
||||
{DisplayName: "All Files", Pattern: "*.*"},
|
||||
},
|
||||
Filters: dbFileFilter(),
|
||||
AllowOtherFileTypes: true,
|
||||
Title: "Create new database",
|
||||
})
|
||||
path, err := d.PromptForSingleSelection()
|
||||
if err != nil {
|
||||
application.InfoDialog().SetMessage("Error: " + err.Error()).Show()
|
||||
}
|
||||
path, _ := d.PromptForSingleSelection()
|
||||
if path != "" {
|
||||
application.InfoDialog().SetMessage(path).Show()
|
||||
} else {
|
||||
application.InfoDialog().SetMessage("No file selected").Show()
|
||||
showError(createDB(path))
|
||||
}
|
||||
})
|
||||
menu.FindByRole(application.Open).OnClick(func(ctx *application.Context) {
|
||||
path, _ := application.OpenFileDialog().
|
||||
CanChooseFiles(true).
|
||||
PromptForSingleSelection()
|
||||
d := application.OpenFileDialog()
|
||||
d.SetOptions(&application.OpenFileDialogOptions{
|
||||
Filters: dbFileFilter(),
|
||||
CanChooseFiles: true,
|
||||
})
|
||||
path, _ := d.PromptForSingleSelection()
|
||||
if path != "" {
|
||||
application.InfoDialog().SetMessage(path).Show()
|
||||
} else {
|
||||
application.InfoDialog().SetMessage("No file selected").Show()
|
||||
showError(openDB(path))
|
||||
}
|
||||
})
|
||||
app.SetMenu(menu)
|
||||
|
@ -4,37 +4,39 @@ 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 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"),
|
||||
hxGet("/hello"),
|
||||
hxTrigger("click"),
|
||||
hxTarget("#hello"),
|
||||
hx.Get("/hello"),
|
||||
hx.Trigger("click"),
|
||||
hx.Target("#hello"),
|
||||
)(h.Text("Click Here!")),
|
||||
e.Div(a.Id("hello"))(),
|
||||
)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Splash() h.Term {
|
||||
return h.Terms{}
|
||||
}
|
||||
|
@ -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,13 +16,11 @@ import (
|
||||
/*
|
||||
Create a new chi router, configure it and return it.
|
||||
*/
|
||||
func NewChiRouter() *chi.Mux {
|
||||
func NewChiRouter(port string, db *peachy.DB) *chi.Mux {
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
// Useful middleware, see : https://pkg.go.dev/github.com/go-chi/httplog/v2@v2.1.1#NewLogger
|
||||
logger := httplog.NewLogger("app-logger", httplog.Options{
|
||||
// All log
|
||||
LogLevel: slog.LevelInfo,
|
||||
Concise: true,
|
||||
})
|
||||
@ -40,29 +40,40 @@ func NewChiRouter() *chi.Mux {
|
||||
})
|
||||
*/
|
||||
|
||||
// Serve static files.
|
||||
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
|
||||
// Home page
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Render home page
|
||||
HXRender(w, r, pages.HomePage)
|
||||
HXRender(w, r, pages.Splash)
|
||||
})
|
||||
|
||||
// 200 OK status
|
||||
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)
|
||||
})
|
||||
|
||||
// Hello page
|
||||
r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Render hello
|
||||
HXRender(w, r, components.HelloWorld)
|
||||
|
||||
// 200 OK status
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
// Listen to port 3000.
|
||||
go http.ListenAndServe(":9245", r)
|
||||
go http.ListenAndServe(":"+port, r)
|
||||
|
||||
return r
|
||||
}
|
||||
|
12
go.mod
12
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
|
||||
|
40
go.sum
40
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=
|
||||
|
54
hx/hx.go
Normal file
54
hx/hx.go
Normal 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
4
metadata_schema.sql
Normal file
@ -0,0 +1,4 @@
|
||||
CREATE TABLE site_metadata (
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
INSERT INTO site_metadata(name) VALUES ('');
|
Loading…
Reference in New Issue
Block a user