feat(recipes): fold progressive-enhancement (subsume ws-disabled)#14
Merged
Conversation
The same controller and template degrade through three transports: Tier A (JS+WS) is the default mount, Tier B is the same handler with WithWebSocketDisabled() at /no-ws/, Tier C is Tier A viewed without JS — the framework's POST-Redirect-GET path falls out of native browser form behavior. Subsuming the ws-disabled example here drops the optional auth-and-fallback composite recipe by one item; the no-WS angle is just one option flag on the same PE app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new “Progressive enhancement: graceful degradation” recipe to the docs site, folding the previous progressive-enhancement / ws-disabled examples into a single app + narrative that demonstrates three transport tiers (WS, fetch fallback, and raw form POST/PRG).
Changes:
- Introduces the new recipe page plus its embedded app package (controller/handler/template) under
content/recipes/progressive-enhancement/. - Mounts the app twice in
cmd/site(/apps/progressive-enhancement/and/apps/progressive-enhancement/no-ws/) and adds the recipe to the docs navigation. - Adds a dedicated E2E suite + local test server under
e2e/progressive-enhancement/covering Tier A/B/C behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| e2e/progressive-enhancement/progressive_enhancement_test.go | New E2E coverage for Tier A (WS), Tier B (no-WS + sentinel), and Tier C (PRG + JSON validation). |
| e2e/progressive-enhancement/main.go | Test-server binary mounting the recipe twice for Tier A/B, configured for dev-mode and permissive origin checks. |
| content/tinkerdown.yaml | Adds the new recipe to the site navigation. |
| content/recipes/progressive-enhancement/index.md | New recipe documentation explaining the 3-tier progressive enhancement story with embeds and code excerpts. |
| content/recipes/progressive-enhancement/_app/progressive-enhancement.tmpl | New template implementing a single form-based UI intended to work across WS/fetch/PRG. |
| content/recipes/progressive-enhancement/_app/handler.go | New handler package exporting Handler(opts...) for mounting under different option sets. |
| content/recipes/progressive-enhancement/_app/controller.go | New controller/state implementing add/toggle/delete + validation and InputTitle persistence. |
| cmd/site/main.go | Mounts the recipe at two routes and factors a shared allowedOrigins list for some recipes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| cleanup := func() { | ||
| if cmd.Process != nil { | ||
| cmd.Process.Kill() |
| <td> | ||
| <form method="POST" name="delete" class="inline"> | ||
| <input type="hidden" name="id" value="{{.ID}}"> | ||
| <button type="submit" name="delete" class="compact contrast outline">✕</button> |
Comment on lines
+56
to
+60
| if success := ctx.GetString("success"); success != "" { | ||
| ctx.SetFlash("success", success) | ||
| } | ||
| if errorMsg := ctx.GetString("error"); errorMsg != "" { | ||
| ctx.SetFlash("error", errorMsg) |
Comment on lines
+508
to
+512
| req.Header.Set("Upgrade", "websocket") | ||
| req.Header.Set("Connection", "Upgrade") | ||
|
|
||
| client := &http.Client{} | ||
| resp, err := client.Do(req) |
Comment on lines
+365
to
+367
| chromedp.WaitReady("body"), | ||
| waitForWSClient(10*time.Second), | ||
| chromedp.OuterHTML("html", &html), |
Comment on lines
+66
to
+70
| allowedOrigins := []string{ | ||
| "https://livetemplate.fly.dev", | ||
| "https://livetemplate-docs-staging.fly.dev", | ||
| "http://localhost:8080", | ||
| "http://localhost:8084", |
| <!-- region:toggle-form --> | ||
| <form method="POST" name="toggle" class="inline"> | ||
| <input type="hidden" name="id" value="{{.ID}}"> | ||
| <button type="submit" name="toggle" class="compact secondary outline"> |
Comment on lines
+76
to
+85
| newID := fmt.Sprintf("%d", time.Now().UnixNano()) | ||
| state.Items = append(state.Items, Todo{ | ||
| ID: newID, | ||
| Title: strings.TrimSpace(input.Title), | ||
| Completed: false, | ||
| CreatedAt: formatTime(), | ||
| }) | ||
|
|
||
| state.InputTitle = "" | ||
| ctx.SetFlash("success", fmt.Sprintf("Added: %s", input.Title)) |
- controller: drop URL query-param → flash bridge in Mount (banner spoof + Tier C uses lvt-flash cookie); use trimmed title in success flash so the list and the banner agree - template: aria-label on toggle (Mark complete/incomplete) and delete buttons for assistive tech - cmd/site: hoist allowedOrigins to one shared list across patterns/todos/PE so the allowlist can't drift - e2e: real gorilla/websocket dial for the no-WS sentinel (a hand-rolled GET with bare Upgrade headers false-passes when WS is enabled); cmd.WaitDelay+Wait to bound the reap (go run's child holds stdout open after SIGKILL); chromedp.ByQuery on Tier B WaitReady/OuterHTML for selector consistency Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
adnaan
added a commit
to livetemplate/livetemplate
that referenced
this pull request
May 10, 2026
Sibling job to test-docs-todos (#402), running the docs/e2e/progressive-enhancement suite against this branch's core changes. Mirrors the todos job verbatim with the working-directory swapped — adds Tier A/B/C coverage (WS, no-WS, raw POST PRG) for the progressive-enhancement recipe folded in livetemplate/docs#14. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
examples/progressive-enhancement/intodocs/content/recipes/progressive-enhancement/_app/and subsumesexamples/ws-disabled/— the only non-trivial difference between the two is one option flag.WithWebSocketDisabled()appended).303 See Otherwith flash cookie).cmd/sitemounts the same handler twice:/apps/progressive-enhancement/and/apps/progressive-enhancement/no-ws/. Bookmarks domain fromexamples/ws-disabled/is dropped — it added nothing pedagogical over a todo list, and unifying around one domain lets the recipe focus the lesson on the option flag rather than two unrelated CRUD apps.docs/e2e/progressive-enhancement/merges the two source examples' tests into Tier A (browser+WS), Tier B (browser+no-WS + WebSocket-rejected sentinel), and Tier C (raw HTTP POST + 303 + JSON validation) groups. Siblingtest-docs-progressive-enhancementcross-repo CI job comes in a follow-up livetemplate PR.Architecture notes
handlerOncesingleton — needed two mounts with different option sets, soHandlerbuilds a freshlivetemplate.New(...) + muxper call. The embedded template is still extracted once viatmplOnce.examples/progressive-enhancement/andexamples/ws-disabled/stay in place per the leave-in-place rule for migrations.sandboxand the e2e tests assert the PRG path directly.Test plan
go build ./...clean (root anddocs/e2e/)X-Livetemplate-Websocket: disabled, Tier B WS upgrade rejected with 400, Tier C POST → 303 +lvt-flashcookiecd docs/e2e/progressive-enhancement && go test -timeout 15m -count=1 ./...— 16 subtests across all three tiers green (~18s)gofmt -landgo vetclean🤖 Generated with Claude Code