diff --git a/.gitignore b/.gitignore index 60e10f5..dd456b9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,18 +37,9 @@ go.work.sum # Air artifacts *tmp/ -# binaries -internal/examples/chatroom/chatroom -internal/examples/counter/counter -internal/examples/countercomp/countercomp -internal/examples/greeter/greeter -internal/examples/livereload/livereload -internal/examples/picocss/picocss -internal/examples/plugins/plugins -internal/examples/realtimechart/realtimechart -internal/examples/shakespeare/shakespeare -internal/examples/nats-chatroom/nats-chatroom -/nats-chatroom +# Example binaries and data files +internal/examples/*/[a-z]*[!.go] +internal/examples/shakespeare/shake.db # NATS data directory data/ diff --git a/README.md b/README.md index 51b2e58..d3b9be6 100644 --- a/README.md +++ b/README.md @@ -74,14 +74,13 @@ func main() { - **Plugin system** — `func(v *V)` hooks for integrating CSS/JS libraries - **Structured logging** — zerolog with configurable levels; console output in dev, JSON in production - **Graceful shutdown** — listens for SIGINT/SIGTERM, drains contexts, closes pub/sub -- **Context lifecycle** — background reaper cleans up disconnected contexts; configurable TTL - **HTML DSL** — the `h` package provides type-safe Go-native HTML composition ## Examples -The `internal/examples/` directory contains 14 runnable examples: +The `internal/examples/` directory contains 19 runnable examples: -`chatroom` · `counter` · `countercomp` · `greeter` · `keyboard` · `livereload` · `nats-chatroom` · `pathparams` · `picocss` · `plugins` · `pubsub-crud` · `realtimechart` · `session` · `shakespeare` +`chatroom` · `counter` · `countercomp` · `effectspike` · `greeter` · `keyboard` · `livereload` · `maplibre` · `middleware` · `nats-chatroom` · `pathparams` · `picocss` · `plugins` · `pubsub-crud` · `realtimechart` · `session` · `shakespeare` · `signup` · `spa` ## Experimental diff --git a/ci-check.sh b/ci-check.sh index 74c481b..0d4d1f4 100755 --- a/ci-check.sh +++ b/ci-check.sh @@ -6,9 +6,13 @@ set -o pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$ROOT" -echo "== CI: Format code ==" -go fmt ./... -echo "OK: formatting complete" +echo "== CI: Check formatting ==" +if [ -n "$(gofmt -l .)" ]; then + echo "ERROR: files not formatted:" + gofmt -l . + exit 1 +fi +echo "OK: all files formatted" echo "== CI: Run go vet ==" if ! go vet ./...; then diff --git a/computed.go b/computed.go index 23ccb25..ad51d44 100644 --- a/computed.go +++ b/computed.go @@ -1,7 +1,6 @@ package via import ( - "fmt" "strconv" "strings" @@ -51,5 +50,5 @@ func (s *computedSignal) recompute() { } func (s *computedSignal) patchValue() string { - return fmt.Sprintf("%v", s.lastVal) + return s.lastVal } diff --git a/docs/state-and-interactivity.md b/docs/state-and-interactivity.md index d743bb7..d4e6578 100644 --- a/docs/state-and-interactivity.md +++ b/docs/state-and-interactivity.md @@ -94,7 +94,7 @@ Available triggers: |--------|-------|-------| | `OnClick()` | `click` | | | `OnDblClick()` | `dblclick` | | -| `OnChange()` | `change` | 200ms debounce | +| `OnChange()` | `change` | | | `OnInput()` | `input` | No debounce | | `OnSubmit()` | `submit` | | | `OnKeyDown(key)` | `keydown` | Filtered by key name (e.g. `"Enter"`, `"Escape"`) | diff --git a/h/h.go b/h/h.go index 5aa2d9d..a5b0d83 100644 --- a/h/h.go +++ b/h/h.go @@ -5,7 +5,7 @@ // // h.Div( // h.H1(h.Text("Hello, Via")), -// h.P(h.Text("Pure Go. No tmplates.")), +// h.P(h.Text("Pure Go. No templates.")), // ) package h diff --git a/internal/examples/livereload/main.go b/internal/examples/livereload/main.go index f1536df..0d26788 100644 --- a/internal/examples/livereload/main.go +++ b/internal/examples/livereload/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/ryanhamamura/via" - // "github.com/go-via/via-plugin-picocss/picocss" "github.com/ryanhamamura/via/h" ) @@ -15,9 +14,6 @@ func main() { DocumentTitle: "Live Reload Demo", DevMode: true, LogLevel: via.LogLevelDebug, - Plugins: []via.Plugin{ - // picocss.Default - }, }) v.Page("/", func(c *via.Context) { diff --git a/internal/examples/pathparams/main.go b/internal/examples/pathparams/main.go index 0088d3b..cc2c823 100644 --- a/internal/examples/pathparams/main.go +++ b/internal/examples/pathparams/main.go @@ -4,17 +4,12 @@ import ( "strconv" "github.com/ryanhamamura/via" - // "github.com/go-via/via-plugin-picocss/picocss" . "github.com/ryanhamamura/via/h" ) func main() { v := via.New() - v.Config(via.Options{ - // Plugins: []via.Plugin{picocss.Default}, - }) - v.Page("/counters/{counter_id}/{start_at_step}", func(c *via.Context) { counterID := c.GetPathParam("counter_id") diff --git a/internal/examples/pathparams/pathparams b/internal/examples/pathparams/pathparams deleted file mode 100755 index 4bc69f2..0000000 Binary files a/internal/examples/pathparams/pathparams and /dev/null differ diff --git a/internal/examples/picocss/main.go b/internal/examples/picocss/main.go index 33b8a14..77f54b1 100644 --- a/internal/examples/picocss/main.go +++ b/internal/examples/picocss/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/ryanhamamura/via" - // "github.com/go-via/via-plugin-picocss/picocss" "github.com/ryanhamamura/via/h" ) @@ -13,11 +12,6 @@ func main() { v.Config(via.Options{ DocumentTitle: "Via Counter", - // Plugin is placed here. Use picocss.WithOptions(pococss.Options) to add the plugin - // with a different color theme or to enable a classes for a wide range of colors. - // Plugins: []via.Plugin{ - // picocss.Default, - // }, }) v.Page("/", func(c *via.Context) { diff --git a/internal/examples/realtimechart/main.go b/internal/examples/realtimechart/main.go index f89bffb..19ced76 100644 --- a/internal/examples/realtimechart/main.go +++ b/internal/examples/realtimechart/main.go @@ -6,7 +6,6 @@ import ( "time" "github.com/ryanhamamura/via" - // "github.com/go-via/via-plugin-picocss/picocss" "github.com/ryanhamamura/via/h" ) @@ -16,9 +15,6 @@ func main() { v.Config(via.Options{ LogLevel: via.LogLevelDebug, DevMode: true, - Plugins: []via.Plugin{ - // picocss.Default, - }, }) v.AppendToHead( diff --git a/internal/examples/shakespeare/main.go b/internal/examples/shakespeare/main.go index 164d603..102d06c 100644 --- a/internal/examples/shakespeare/main.go +++ b/internal/examples/shakespeare/main.go @@ -22,6 +22,9 @@ type ShakeDB struct { findByTextStmt *sql.Stmt } +// Prepare opens shake.db, a ~22 MB SQLite database of Shakespeare's works. +// Download from https://github.com/nicholasgasior/gopher-fizzbuzz/raw/master/shake.db +// and place it in this directory before running. func (shakeDB *ShakeDB) Prepare() { db, err := sql.Open("sqlite3", "shake.db") if err != nil { diff --git a/internal/examples/shakespeare/shake.db b/internal/examples/shakespeare/shake.db deleted file mode 100644 index 8abddfc..0000000 Binary files a/internal/examples/shakespeare/shake.db and /dev/null differ diff --git a/signal_test.go b/signal_test.go index b7ccb14..4087a53 100644 --- a/signal_test.go +++ b/signal_test.go @@ -1,7 +1,6 @@ package via import ( - // "net/http/httptest" "testing" "github.com/ryanhamamura/via/h" diff --git a/via.go b/via.go index 4ad1cdf..d044aa3 100644 --- a/via.go +++ b/via.go @@ -439,36 +439,39 @@ func (v *V) ensureDatastarHandler() { }) } +func loadDevModeMap(path string) map[string]string { + m := make(map[string]string) + file, err := os.Open(path) + if err != nil { + return m + } + defer file.Close() + json.NewDecoder(file).Decode(&m) + return m +} + +func saveDevModeMap(path string, m map[string]string) error { + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + return json.NewEncoder(file).Encode(m) +} + func (v *V) devModePersist(c *Context) { p := filepath.Join(".via", "devmode", "ctx.json") if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { v.logFatal("failed to create directory for devmode files: %v", err) } - // load persisted list from file, or empty list if file not found - file, err := os.Open(p) - ctxRegMap := make(map[string]string) - if err == nil { - json.NewDecoder(file).Decode(&ctxRegMap) - } - file.Close() - - // add ctx to persisted list + ctxRegMap := loadDevModeMap(p) if _, ok := ctxRegMap[c.id]; !ok { ctxRegMap[c.id] = c.route } - // write persisted list to file - file, err = os.Create(p) - if err != nil { - v.logErr(c, "devmode failed to percist ctx: %v", err) - - } - defer file.Close() - - encoder := json.NewEncoder(file) - if err := encoder.Encode(ctxRegMap); err != nil { - v.logErr(c, "devmode failed to persist ctx") + if err := saveDevModeMap(p, ctxRegMap); err != nil { + v.logErr(c, "devmode failed to persist ctx: %v", err) } v.logDebug(c, "devmode persisted ctx to file") } @@ -476,27 +479,11 @@ func (v *V) devModePersist(c *Context) { func (v *V) devModeRemovePersisted(c *Context) { p := filepath.Join(".via", "devmode", "ctx.json") - // load persisted list from file, or empty list if file not found - file, err := os.Open(p) - ctxRegMap := make(map[string]string) - if err == nil { - json.NewDecoder(file).Decode(&ctxRegMap) - } - file.Close() - + ctxRegMap := loadDevModeMap(p) delete(ctxRegMap, c.id) - // write persisted list to file - file, err = os.Create(p) - if err != nil { - v.logErr(c, "devmode failed to remove percisted ctx: %v", err) - - } - defer file.Close() - - encoder := json.NewEncoder(file) - if err := encoder.Encode(ctxRegMap); err != nil { - v.logErr(c, "devmode failed to remove persisted ctx") + if err := saveDevModeMap(p, ctxRegMap); err != nil { + v.logErr(c, "devmode failed to remove persisted ctx: %v", err) } v.logDebug(c, "devmode removed persisted ctx from file") }