diff --git a/avatar-upload/README.md b/avatar-upload/README.md new file mode 100644 index 0000000..8a082e0 --- /dev/null +++ b/avatar-upload/README.md @@ -0,0 +1,166 @@ +# Avatar Upload Example + +A simple example demonstrating LiveTemplate's file upload feature with avatar upload functionality. + +## Features + +- 📸 **Image Upload**: Upload JPEG, PNG, or GIF avatars +- 📊 **Real-time Progress**: WebSocket chunked upload with live progress tracking +- ✅ **Validation**: Automatic file type and size validation (5MB limit) +- 🎨 **Beautiful UI**: Gradient design with smooth animations +- 🔄 **Live Updates**: Profile updates instantly without page reload + +## What This Example Demonstrates + +### Upload Configuration +```go +func (s *ProfileStore) AllowUploads() map[string]livetemplate.UploadConfig { + return map[string]livetemplate.UploadConfig{ + "avatar": { + Accept: []string{"image/jpeg", "image/png", "image/gif"}, + MaxFileSize: 5 * 1024 * 1024, // 5MB + MaxEntries: 1, // Single file + AutoUpload: false, // Manual upload on form submit + ChunkSize: 256 * 1024, // 256KB chunks + }, + } +} +``` + +### Upload Processing +```go +func (s *ProfileStore) ConsumeUpload(ctx context.Context, name string, entries []*livetemplate.UploadEntry) error { + for _, entry := range entries { + // Move from temp to permanent location + permanentPath := filepath.Join("uploads", fmt.Sprintf("avatar-%s%s", entry.ID, ext)) + os.Rename(entry.TempPath, permanentPath) + + // Update store with new avatar + s.AvatarPath = permanentPath + s.AvatarURL = "/" + permanentPath + } + return nil +} +``` + +### Template Helpers +```html + + + +{{range .lvt.Uploads "avatar"}} +
+ {{.ClientName}} - {{.Progress}}% + + {{if .Error}}{{.Error}}{{end}} +
+{{end}} +``` + +## Running the Example + +### 1. Install Dependencies + +```bash +cd /Users/adnaan/code/livetemplate/examples/avatar-upload +go mod download +``` + +### 2. Run the Server + +```bash +go run main.go +``` + +The server will start at http://localhost:8080 + +### 3. Try It Out + +1. Open http://localhost:8080 in your browser +2. Click "Choose File" and select an image (JPEG, PNG, or GIF) +3. Click "Save Profile" +4. Watch the real-time progress bar as your file uploads +5. See your avatar appear instantly when upload completes! + +## Upload Strategies + +This example uses **WebSocket Chunked Upload**: +- ✅ Real-time progress tracking +- ✅ Handles large files efficiently (256KB chunks) +- ✅ Non-blocking uploads +- ✅ Works with LiveTemplate's reactive updates + +## File Structure + +``` +avatar-upload/ +├── main.go # Server code with ProfileStore +├── avatar-upload.tmpl # HTML template with upload UI +├── go.mod # Dependencies (uses local livetemplate) +├── README.md # This file +└── uploads/ # Created at runtime for uploaded avatars +``` + +## Testing Different Scenarios + +### Valid Upload +- Upload a JPEG, PNG, or GIF under 5MB +- ✅ Should show progress and complete successfully + +### File Too Large +- Upload an image over 5MB +- ❌ Should show validation error + +### Invalid File Type +- Upload a non-image file (e.g., .txt, .pdf) +- ❌ Should show "file type not accepted" error + +### Multiple Files +- Try selecting multiple images +- â„šī¸ Only the first will be accepted (MaxEntries: 1) + +## Code Quality + +This example demonstrates: +- ✅ Clean separation of concerns (Store pattern) +- ✅ Proper error handling +- ✅ File validation and security +- ✅ Temp file cleanup +- ✅ LiveTemplate best practices + +## Next Steps + +Want to extend this example? + +1. **Add S3 Upload**: Replace local storage with S3 presigner +2. **Multiple Avatars**: Change `MaxEntries` to allow multiple images +3. **Image Cropping**: Add client-side cropping before upload +4. **Drag & Drop**: Add drag-and-drop file selection +5. **Auto-Upload**: Set `AutoUpload: true` for instant uploads + +## Learn More + +- [Upload Documentation](../../livetemplate/.worktrees/feature-uploads/docs/uploads.md) +- [LiveTemplate Documentation](https://github.com/livetemplate/livetemplate) +- [Other Examples](../) + +## Troubleshooting + +**Upload not working?** +- Check browser console for errors +- Ensure WebSocket connection is established (look for green indicator) +- Verify file meets validation criteria (type, size) + +**Progress not updating?** +- Make sure you're using WebSocket (not HTTP fallback) +- Check that ChunkSize is set appropriately +- Verify client library is loaded + +**Files not saving?** +- Check that `uploads/` directory exists (created automatically) +- Verify file permissions on the uploads directory +- Check server logs for errors + +--- + +Built with â¤ī¸ using [LiveTemplate v0.3.0](https://github.com/livetemplate/livetemplate) diff --git a/avatar-upload/avatar-upload.tmpl b/avatar-upload/avatar-upload.tmpl new file mode 100644 index 0000000..30a7983 --- /dev/null +++ b/avatar-upload/avatar-upload.tmpl @@ -0,0 +1,317 @@ + + + + + + Avatar Upload Example - LiveTemplate + + + +
+

Profile Settings

+

Upload your avatar and update your profile

+ +
+ 📸 Accepted: JPEG, PNG, GIF â€ĸ Max size: 5MB â€ĸ WebSocket chunked upload with real-time progress +
+ +
+
+ {{if .AvatarURL}} + Avatar + {{else}} + {{slice .Name 0 1}} + {{end}} +
+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ {{range .lvt.Uploads "avatar"}} +
+
+ {{.ClientName}} + {{.Progress}}% +
+
+
+
+ {{if .Error}} +
❌ {{.Error}}
+ {{else if .Done}} +
✅ Upload complete!
+ {{end}} +
+ {{end}} + + {{if .lvt.HasUploadError "avatar"}} +
âš ī¸ {{.lvt.UploadError "avatar"}}
+ {{end}} +
+ + +
+ +
+ Powered by LiveTemplate v0.3.0 +
+
+ + + + + diff --git a/avatar-upload/go.mod b/avatar-upload/go.mod new file mode 100644 index 0000000..3d094e2 --- /dev/null +++ b/avatar-upload/go.mod @@ -0,0 +1,25 @@ +module github.com/livetemplate/examples/avatar-upload + +go 1.25.3 + +require github.com/livetemplate/livetemplate v0.3.0 + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.28.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/redis/go-redis/v9 v9.16.0 // indirect + github.com/tdewolff/minify/v2 v2.24.3 // indirect + github.com/tdewolff/parse/v2 v2.8.3 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect +) diff --git a/avatar-upload/go.sum b/avatar-upload/go.sum new file mode 100644 index 0000000..1ea0169 --- /dev/null +++ b/avatar-upload/go.sum @@ -0,0 +1,146 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= +github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= +github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/redis/go-redis/v9 v9.16.0 h1:OotgqgLSRCmzfqChbQyG1PHC3tLNR89DG4jdOERSEP4= +github.com/redis/go-redis/v9 v9.16.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= +github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= +github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= +github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tdewolff/minify/v2 v2.24.3 h1:BaKgWSFLKbKDiUskbeRgbe2n5d1Ci1x3cN/eXna8zOA= +github.com/tdewolff/minify/v2 v2.24.3/go.mod h1:1JrCtoZXaDbqioQZfk3Jdmr0GPJKiU7c1Apmb+7tCeE= +github.com/tdewolff/parse/v2 v2.8.3 h1:5VbvtJ83cfb289A1HzRA9sf02iT8YyUwN84ezjkdY1I= +github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= +github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts= +github.com/testcontainers/testcontainers-go v0.39.0/go.mod h1:qmHpkG7H5uPf/EvOORKvS6EuDkBUPE3zpVGaH9NL7f8= +github.com/testcontainers/testcontainers-go/modules/redis v0.39.0 h1:p54qELdCx4Gftkxzf44k9RJRRhaO/S5ehP9zo8SUTLM= +github.com/testcontainers/testcontainers-go/modules/redis v0.39.0/go.mod h1:P1mTbHruHqAU2I26y0RADz1BitF59FLbQr7ceqN9bt4= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +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/avatar-upload/main.go b/avatar-upload/main.go new file mode 100644 index 0000000..1870754 --- /dev/null +++ b/avatar-upload/main.go @@ -0,0 +1,133 @@ +package main + +import ( + "context" + "embed" + "fmt" + "log" + "net/http" + "os" + "path/filepath" + + "github.com/livetemplate/livetemplate" +) + +//go:embed *.tmpl +var templates embed.FS + +// ProfileStore manages user profile with avatar upload +type ProfileStore struct { + Name string + Email string + AvatarPath string + AvatarURL string +} + +// AllowUploads configures avatar upload +func (s *ProfileStore) AllowUploads() map[string]livetemplate.UploadConfig { + return map[string]livetemplate.UploadConfig{ + "avatar": { + Accept: []string{"image/jpeg", "image/png", "image/gif"}, + MaxFileSize: 5 * 1024 * 1024, // 5MB + MaxEntries: 1, // Single file + AutoUpload: false, // Manual upload on form submit + ChunkSize: 256 * 1024, // 256KB chunks + }, + } +} + +// ConsumeUpload processes uploaded avatar +func (s *ProfileStore) ConsumeUpload(ctx context.Context, name string, entries []*livetemplate.UploadEntry) error { + if name != "avatar" { + return nil + } + + // Create uploads directory if it doesn't exist + uploadsDir := "uploads" + if err := os.MkdirAll(uploadsDir, 0755); err != nil { + return fmt.Errorf("failed to create uploads directory: %w", err) + } + + for _, entry := range entries { + // Generate permanent filename + ext := filepath.Ext(entry.ClientName) + permanentPath := filepath.Join(uploadsDir, fmt.Sprintf("avatar-%s%s", entry.ID, ext)) + + // Move from temp to permanent location + if err := os.Rename(entry.TempPath, permanentPath); err != nil { + // If rename fails (different filesystem), try copy + if err := copyFile(entry.TempPath, permanentPath); err != nil { + return fmt.Errorf("failed to save avatar: %w", err) + } + os.Remove(entry.TempPath) // Clean up temp file + } + + // Update store with new avatar + s.AvatarPath = permanentPath + s.AvatarURL = "/" + permanentPath + + log.Printf("Avatar saved: %s (original: %s, size: %d bytes)", permanentPath, entry.ClientName, entry.ClientSize) + } + + return nil +} + +// copyFile copies a file from src to dst +func copyFile(src, dst string) error { + data, err := os.ReadFile(src) + if err != nil { + return err + } + return os.WriteFile(dst, data, 0644) +} + +// UpdateProfile handles profile update form submission +func (s *ProfileStore) UpdateProfile(ctx context.Context, data livetemplate.ActionData) error { + name, _ := data.String("name") + email, _ := data.String("email") + + s.Name = name + s.Email = email + + log.Printf("Profile updated: name=%s, email=%s", s.Name, s.Email) + return nil +} + +func main() { + // Parse port from environment or use default + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + // Create LiveTemplate instance + lt := livetemplate.Must(livetemplate.New("avatar-upload", + livetemplate.WithTemplateFS(templates), + livetemplate.WithDevMode(true), + ) + + // Create initial store + store := &ProfileStore{ + Name: "John Doe", + Email: "john@example.com", + } + + // Create handler with upload support + handler := lt.NewHandler(store) + + // Serve static files (for uploaded avatars) + http.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("uploads")))) + + // Mount the LiveTemplate handler + http.Handle("/", handler) + + // Start server + addr := ":" + port + log.Printf("🚀 Avatar upload example running at http://localhost%s", addr) + log.Printf("📸 Upload an avatar to see the upload feature in action!") + log.Printf("📁 Uploaded files will be saved to ./uploads/") + + if err := http.ListenAndServe(addr, nil); err != nil { + log.Fatal(err) + } +} diff --git a/avatar-upload/run.sh b/avatar-upload/run.sh new file mode 100755 index 0000000..99f21a5 --- /dev/null +++ b/avatar-upload/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Simple script to run the avatar upload example + +cd "$(dirname "$0")" + +echo "🚀 Starting Avatar Upload Example..." +echo "📍 Server will run at http://localhost:8082" +echo "" + +# Set PORT and run +PORT=8082 go run main.go diff --git a/chat/go.mod b/chat/go.mod index fca41b0..98ea487 100644 --- a/chat/go.mod +++ b/chat/go.mod @@ -4,7 +4,7 @@ go 1.25 require ( github.com/chromedp/chromedp v0.14.2 - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 ) require ( @@ -30,6 +30,7 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) // TODO: Add github.com/livetemplate/lvt v0.1.0 after it's tagged diff --git a/chat/go.sum b/chat/go.sum index c06aa39..c26c4d1 100644 --- a/chat/go.sum +++ b/chat/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= @@ -159,5 +159,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/chat/main.go b/chat/main.go index ff856a7..9f73f26 100644 --- a/chat/main.go +++ b/chat/main.go @@ -148,7 +148,7 @@ func main() { // Uses default AnonymousAuthenticator - each browser gets its own session (via cookie) // Tabs in same browser share state // Configure via LVT_* environment variables (e.g., LVT_DEV_MODE=true) - tmpl := livetemplate.New("chat", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("chat", envConfig.ToOptions()...)) // Mount handler http.Handle("/", tmpl.Handle(state)) diff --git a/counter/go.mod b/counter/go.mod index b858890..5b7d691 100644 --- a/counter/go.mod +++ b/counter/go.mod @@ -5,7 +5,7 @@ go 1.25 require ( github.com/chromedp/chromedp v0.14.2 github.com/gorilla/websocket v1.5.3 - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 ) @@ -31,4 +31,5 @@ require ( golang.org/x/net v0.46.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/text v0.30.0 // indirect + golang.org/x/time v0.14.0 // indirect ) diff --git a/counter/go.sum b/counter/go.sum index ef2b61e..ddb7b28 100644 --- a/counter/go.sum +++ b/counter/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 h1:WQcir+LsmAlaihnnRdLUiGefy2J+cnpoRMrzJgNoF4k= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0/go.mod h1:+MkJVOR/rgGWe6xEVnjRSRCKhjM1KkpeaheqS9UHjM8= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/counter/main.go b/counter/main.go index 06e4903..59c90de 100644 --- a/counter/main.go +++ b/counter/main.go @@ -60,7 +60,7 @@ func main() { // Create template with environment-based configuration // Configuration is loaded from LVT_* environment variables - tmpl := livetemplate.New("counter", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("counter", envConfig.ToOptions()...)) // Mount handler - auto-handles initial page, WebSocket, and HTTP actions http.Handle("/", tmpl.Handle(state)) diff --git a/graceful-shutdown/go.mod b/graceful-shutdown/go.mod index fa9b915..e4768aa 100644 --- a/graceful-shutdown/go.mod +++ b/graceful-shutdown/go.mod @@ -3,7 +3,7 @@ module graceful-shutdown go 1.25 require ( - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 ) @@ -31,4 +31,5 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) diff --git a/graceful-shutdown/go.sum b/graceful-shutdown/go.sum index d0f43c1..090c207 100644 --- a/graceful-shutdown/go.sum +++ b/graceful-shutdown/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 h1:WQcir+LsmAlaihnnRdLUiGefy2J+cnpoRMrzJgNoF4k= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0/go.mod h1:+MkJVOR/rgGWe6xEVnjRSRCKhjM1KkpeaheqS9UHjM8= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/graceful-shutdown/main.go b/graceful-shutdown/main.go index 551f943..8ca92ad 100644 --- a/graceful-shutdown/main.go +++ b/graceful-shutdown/main.go @@ -63,7 +63,7 @@ func main() { } // Create template with environment-based configuration - tmpl := livetemplate.New("counter", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("counter", envConfig.ToOptions()...)) // Get the LiveHandler for shutdown control handler := tmpl.Handle(state) diff --git a/observability/go.mod b/observability/go.mod index 2f0e03a..6fdb33c 100644 --- a/observability/go.mod +++ b/observability/go.mod @@ -3,7 +3,7 @@ module observability go 1.25 require ( - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da ) @@ -31,6 +31,7 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) // TODO: Add github.com/livetemplate/lvt v0.1.0 after it's tagged diff --git a/observability/go.sum b/observability/go.sum index 1ad4ca6..8fbd5b2 100644 --- a/observability/go.sum +++ b/observability/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da h1:ZONdEpB9vxj4xyJlQ1t3I/sIxkllrKu5fWadFL60nsA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da/go.mod h1:1/HRPggC3loru3AB6GYQdMWP1bJhuCGZERD36GlLEw0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/observability/main.go b/observability/main.go index ed05fb2..c68b512 100644 --- a/observability/main.go +++ b/observability/main.go @@ -119,7 +119,7 @@ func main() { // Create template with environment-based configuration // Template operations are now automatically logged! // Configuration is loaded from LVT_* environment variables - tmpl := livetemplate.New("counter", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("counter", envConfig.ToOptions()...)) // Mount handler - auto-handles initial page, WebSocket, and HTTP actions // All actions and WebSocket events are now logged and metered! diff --git a/production/single-host/go.mod b/production/single-host/go.mod index b1660c2..be593f9 100644 --- a/production/single-host/go.mod +++ b/production/single-host/go.mod @@ -3,7 +3,7 @@ module single-host go 1.25 require ( - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da ) @@ -31,6 +31,7 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) // TODO: Add github.com/livetemplate/lvt v0.1.0 after it's tagged diff --git a/production/single-host/go.sum b/production/single-host/go.sum index 1ad4ca6..8fbd5b2 100644 --- a/production/single-host/go.sum +++ b/production/single-host/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da h1:ZONdEpB9vxj4xyJlQ1t3I/sIxkllrKu5fWadFL60nsA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da/go.mod h1:1/HRPggC3loru3AB6GYQdMWP1bJhuCGZERD36GlLEw0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/production/single-host/main.go b/production/single-host/main.go index af8c910..e0210d4 100644 --- a/production/single-host/main.go +++ b/production/single-host/main.go @@ -93,7 +93,7 @@ func main() { } // Create template - tmpl := livetemplate.New("app", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("app", envConfig.ToOptions()...)) liveHandler := tmpl.Handle(state) // Setup HTTP routes with trace middleware diff --git a/testing/01_basic/go.mod b/testing/01_basic/go.mod index 6c0d761..2ce2d1b 100644 --- a/testing/01_basic/go.mod +++ b/testing/01_basic/go.mod @@ -3,7 +3,7 @@ module basic go 1.25 require ( - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 ) @@ -31,6 +31,7 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) // TODO: Add github.com/livetemplate/lvt v0.1.0 after it's tagged diff --git a/testing/01_basic/go.sum b/testing/01_basic/go.sum index d0f43c1..090c207 100644 --- a/testing/01_basic/go.sum +++ b/testing/01_basic/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 h1:WQcir+LsmAlaihnnRdLUiGefy2J+cnpoRMrzJgNoF4k= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0/go.mod h1:+MkJVOR/rgGWe6xEVnjRSRCKhjM1KkpeaheqS9UHjM8= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/testing/01_basic/main.go b/testing/01_basic/main.go index 25cdc05..7f3280c 100644 --- a/testing/01_basic/main.go +++ b/testing/01_basic/main.go @@ -22,26 +22,8 @@ func (s *PageState) Change(ctx *livetemplate.ActionContext) error { } func main() { - // Create template - tmpl := livetemplate.New("welcome") - - // Parse template inline - if _, err := tmpl.Parse(` - - - - {{.Title}} - - -

{{.Title}}

-

{{.Message}}

-

Count: {{.Count}}

- - - - `); err != nil { - log.Fatal(err) - } + // Create template (will auto-discover welcome.tmpl) + tmpl := livetemplate.Must(livetemplate.New("welcome")) // Create state state := &PageState{ diff --git a/testing/01_basic/welcome.tmpl b/testing/01_basic/welcome.tmpl new file mode 100644 index 0000000..b2e8003 --- /dev/null +++ b/testing/01_basic/welcome.tmpl @@ -0,0 +1,12 @@ + + + + {{.Title}} + + +

{{.Title}}

+

{{.Message}}

+

Count: {{.Count}}

+ + + diff --git a/todos/go.mod b/todos/go.mod index e1bd64a..0d42914 100644 --- a/todos/go.mod +++ b/todos/go.mod @@ -6,7 +6,7 @@ require ( github.com/chromedp/chromedp v0.14.2 github.com/go-playground/validator/v10 v10.28.0 github.com/gorilla/websocket v1.5.3 - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 modernc.org/sqlite v1.39.1 ) @@ -37,6 +37,7 @@ require ( golang.org/x/net v0.46.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/text v0.30.0 // indirect + golang.org/x/time v0.14.0 // indirect modernc.org/libc v1.66.10 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect diff --git a/todos/go.sum b/todos/go.sum index 4f9f7c0..49e92b3 100644 --- a/todos/go.sum +++ b/todos/go.sum @@ -84,8 +84,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0 h1:WQcir+LsmAlaihnnRdLUiGefy2J+cnpoRMrzJgNoF4k= github.com/livetemplate/lvt v0.0.0-20251103195948-fbcd6dfae2d0/go.mod h1:+MkJVOR/rgGWe6xEVnjRSRCKhjM1KkpeaheqS9UHjM8= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -177,6 +177,8 @@ golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/todos/main.go b/todos/main.go index 7571b2d..7526262 100644 --- a/todos/main.go +++ b/todos/main.go @@ -374,7 +374,7 @@ func main() { // Create template with environment-based configuration // Configuration is loaded from LVT_* environment variables - tmpl := livetemplate.New("todos", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("todos", envConfig.ToOptions()...)) // Mount handler - auto-handles initial page, WebSocket, and HTTP actions http.Handle("/", tmpl.Handle(state)) diff --git a/trace-correlation/go.mod b/trace-correlation/go.mod index 0f0d7c2..da58973 100644 --- a/trace-correlation/go.mod +++ b/trace-correlation/go.mod @@ -3,7 +3,7 @@ module trace-correlation go 1.25 require ( - github.com/livetemplate/livetemplate v0.1.1 + github.com/livetemplate/livetemplate v0.3.0 github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da ) @@ -31,6 +31,7 @@ require ( golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.14.0 // indirect ) // TODO: Add github.com/livetemplate/lvt v0.1.0 after it's tagged diff --git a/trace-correlation/go.sum b/trace-correlation/go.sum index 1ad4ca6..8fbd5b2 100644 --- a/trace-correlation/go.sum +++ b/trace-correlation/go.sum @@ -80,8 +80,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/livetemplate/livetemplate v0.1.1 h1:DS8VXLOvdHqtSbk+Zr6+znCHgRvU9qD6os3ABY9DOlA= -github.com/livetemplate/livetemplate v0.1.1/go.mod h1:SobBok7X09b1oxEIHEAvABTRhGqRjCfMpUAY7E7XeHg= +github.com/livetemplate/livetemplate v0.3.0 h1:k3cpsiOmWcjS+S/e5rvydbi6nY86ipnWd801B5hgL5M= +github.com/livetemplate/livetemplate v0.3.0/go.mod h1:PYS1hH2a6o7d9LID96780l/HHSGJIJS70kBnVO1mIUA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da h1:ZONdEpB9vxj4xyJlQ1t3I/sIxkllrKu5fWadFL60nsA= github.com/livetemplate/lvt v0.0.0-20251103070549-7ffea37f50da/go.mod h1:1/HRPggC3loru3AB6GYQdMWP1bJhuCGZERD36GlLEw0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -161,5 +161,7 @@ golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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/trace-correlation/main.go b/trace-correlation/main.go index d681be2..7000748 100644 --- a/trace-correlation/main.go +++ b/trace-correlation/main.go @@ -97,7 +97,7 @@ func main() { } // Create template - tmpl := livetemplate.New("counter", envConfig.ToOptions()...) + tmpl := livetemplate.Must(livetemplate.New("counter", envConfig.ToOptions()...)) liveHandler := tmpl.Handle(state) // Setup HTTP routes with trace middleware