diff --git a/CLAUDE.md b/CLAUDE.md
index 9d7a1f8..ad0846a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -22,19 +22,20 @@ All examples follow the **progressive complexity** model introduced in livetempl
| Attribute | Purpose | Example |
|-----------|---------|---------|
-| `lvt-scroll` | Auto-scroll behavior | Chat message container |
+| `lvt-fx:scroll` | Auto-scroll behavior | Chat message container |
| `lvt-upload` | Chunked file uploads | Avatar upload |
-| `lvt-debounce` | Custom timing control | Search with custom delay |
-| `lvt-keydown` | Keyboard shortcuts | Global key bindings |
-| `lvt-animate` | Entry/exit animations | Toast notifications |
+| `lvt-mod:debounce` | Custom timing control | Search with custom delay |
+| `lvt-on:keydown` | Keyboard shortcuts | Global key bindings |
+| `lvt-fx:animate` | Entry/exit animations | Toast notifications |
+| `lvt-form:preserve` | Preserve form state across re-renders | Shared notepad |
+| `lvt-form:no-intercept` | Skip WebSocket, use real HTTP POST | Login/logout forms |
### Action Resolution Order
When a form is submitted, the framework resolves the action in this order:
-1. `lvt-submit` attribute on the form
-2. Clicked button's `name` attribute
-3. Form's `name` attribute
-4. Default: `"submit"`
+1. Clicked button's `name` attribute
+2. Form's `name` attribute
+3. Default: `"submit"`
## Creating New Examples
@@ -65,7 +66,7 @@ When a form is submitted, the framework resolves the action in this order:
- `todos/` — Canonical Tier 1 example: CRUD, auth, pagination, modal + toast components
- `live-preview/` — Tier 1 with `Change()` method for live updates
-- `chat/` — Tier 1+2 (uses `lvt-scroll` for auto-scroll)
+- `chat/` — Tier 1+2 (uses `lvt-fx:scroll` for auto-scroll)
## Framework Documentation
diff --git a/README.md b/README.md
index 8072da3..246f002 100644
--- a/README.md
+++ b/README.md
@@ -9,15 +9,15 @@ All examples follow the [progressive complexity](https://github.com/livetemplate
| Example | Tier | Description | Tier 2 Attributes |
|---------|------|-------------|--------------------|
| `counter/` | 1 | Counter with logging + graceful shutdown | None |
-| `chat/` | 1+2 | Real-time multi-user chat | `lvt-scroll` |
+| `chat/` | 1+2 | Real-time multi-user chat | `lvt-fx:scroll` |
| `todos/` | 1+2 | Full CRUD with SQLite, auth, modal + toast components | Component-internal |
| `flash-messages/` | 1 | Flash notification patterns | None |
| `avatar-upload/` | 1+2 | File upload with progress | `lvt-upload` |
| `progressive-enhancement/` | 1 | Works with/without JS | None |
| `ws-disabled/` | 1 | HTTP-only mode | None |
| `live-preview/` | 1 | Change() live updates | None |
-| `login/` | 1 | Authentication + sessions | None |
-| `shared-notepad/` | 1 | BasicAuth + SharedState | None |
+| `login/` | 1+2 | Authentication + sessions | `lvt-form:no-intercept` |
+| `shared-notepad/` | 1+2 | BasicAuth + SharedState | `lvt-form:preserve` |
## Examples
@@ -77,7 +77,7 @@ For local development, examples can serve the client library locally using `gith
## Dependencies
-- **Core Library**: `github.com/livetemplate/livetemplate v0.8.7`
+- **Core Library**: `github.com/livetemplate/livetemplate v0.8.15`
- **LVT Testing** (for examples with E2E tests): `github.com/livetemplate/lvt` (latest)
- **Client Library**: `@livetemplate/client@latest` (via CDN)
diff --git a/avatar-upload/avatar-upload.tmpl b/avatar-upload/avatar-upload.tmpl
index b7a623d..10b9537 100644
--- a/avatar-upload/avatar-upload.tmpl
+++ b/avatar-upload/avatar-upload.tmpl
@@ -26,7 +26,7 @@
{{end}}
-
{{else}}
-
+
{{if eq (len .Messages) 0}}
No messages yet. Be the first to send one!
diff --git a/counter/README.md b/counter/README.md
index a85e80b..98c7c76 100644
--- a/counter/README.md
+++ b/counter/README.md
@@ -85,71 +85,71 @@ For more details, see [CONFIGURATION.md](../../docs/CONFIGURATION.md).
The server is extremely simple with the new reactive API:
```go
+// Controller: singleton, holds dependencies (none in this simple example)
+type CounterController struct{}
+
+// State: pure data, cloned per session
type CounterState struct {
- Counter int `json:"counter"`
- Status string `json:"status"`
- // ... other fields
+ Title string `json:"title" lvt:"persist"`
+ Counter int `json:"counter" lvt:"persist"`
+ LastUpdated string `json:"last_updated" lvt:"persist"`
}
-// Implement the Store interface
-func (s *CounterState) Change(action string, data map[string]interface{}) {
- switch action {
- case "increment":
- s.Counter++
- case "decrement":
- s.Counter--
- case "reset":
- s.Counter = 0
- }
-
- // Update derived state
- s.Status = getStatus(s.Counter)
- s.LastUpdated = formatTime()
+// Named action methods — routed via