Skip to content

feat(recipes): fold login + shared-notepad as deep-dive recipes#19

Open
adnaan wants to merge 1 commit into
mainfrom
recipes-login-and-shared-notepad-fold
Open

feat(recipes): fold login + shared-notepad as deep-dive recipes#19
adnaan wants to merge 1 commit into
mainfrom
recipes-login-and-shared-notepad-fold

Conversation

@adnaan
Copy link
Copy Markdown
Contributor

@adnaan adnaan commented May 13, 2026

Summary

Path X migration completes — login and shared-notepad join counter, todos, and progressive-enhancement as deep-dive recipes under content/recipes/<slug>/. After this lands, zero must-do migrations remain on the backlog.

Two new recipes, embedded inline via tinkerdown's embed-lvt block alongside literate include= source citations:

  • recipes/login/ — form-based session auth: lvt-form:no-intercept POST + ctx.SetCookie + 303 redirect + OnConnect goroutine + session.TriggerAction("serverWelcome", ...) for the server-pushed welcome message. The "everything happens in one page" path that comes for free when you don't reach for a separate auth middleware.

  • recipes/shared-notepad/ — BasicAuth + per-user state map keyed by ctx.UserID() + explicit ctx.BroadcastAction("Refresh", nil) for peer-tab refresh. The Refresh action replaces the auto-Sync method removed in livetemplate#406; recipe text walks the migration explicitly.

Auth choice for the shared-notepad embed

The shared-notepad handler ships two authenticator flavours via NewDemoBasicAuth (a function callers pick at mount time) and a documented Anonymous fallback. The recipe text TEACHES the BasicAuth shape (via include= from _app/handler.go); the e2e suite + examples/shared-notepad/ exercise it.

The cmd/site mount, however, wires AnonymousAuthenticator instead — because tinkerdown's embed-lvt does a server-side prefetch to extract the LiveTemplate wrapper, and that prefetch can't forward Authorization headers. A BasicAuth mount degrades to "live demo unavailable" in the embed; AnonymousAuthenticator gives each browser its own cookie-bound session group so the multi-tab BroadcastAction demo works in-page, and the cross-identity isolation story surfaces by opening a private window. A blockquote on the recipe page spells the trade out honestly — controller code is identical under both authenticators, only ctx.UserID()'s source changes.

Test plan

  • go build ./... + go vet + golangci-lint run clean across cmd/site, content/recipes/login/_app, content/recipes/shared-notepad/_app
  • cd e2e/login && go test ./... — 8/8 subtests green in 16.5s (incl. ServerPushedWelcome regression guard for ctx.Session() non-nil and TestLogin_HTTPCookie raw-HTTP cookie shape)
  • cd e2e/shared-notepad && go test ./... — 4/4 subtests green in 7.7s (incl. new TestSharedNotepad_MultiUserIsolation — HTTP-only, two clients under the BasicAuth flavour, asserts neither sees the other's text)
  • cmd/site curl smokes: counter/todos/PE/login/shared-notepad/ui-patterns all respond correctly
  • iPhone manual via Tailscale on /recipes/login/ and /recipes/shared-notepad/ — both embeds render and exercise the full demo flow inline; the multi-tab BroadcastAction("Refresh", nil) propagation visible by opening the recipe page in a second tab; cross-identity isolation visible via private window. Signed off.

Follow-ups

The CI sibling PR (test-docs-login + test-docs-shared-notepad jobs in livetemplate/.github/workflows/cross-repo-test.yml) follows in livetemplate, opened after this lands so the working directories exist on docs main first — same chicken-and-egg ordering used for todos and PE.

🤖 Generated with Claude Code

Path X migration completes — login and shared-notepad join counter,
todos, and progressive-enhancement under content/recipes/<slug>/.

Two new recipes, embedded inline via tinkerdown's embed-lvt block
alongside literate include= source citations:

- recipes/login/ — form-based session auth: lvt-form:no-intercept
  POST + ctx.SetCookie + 303 redirect + OnConnect goroutine +
  session.TriggerAction("serverWelcome", ...) for the server-pushed
  welcome message. Cookie-driven identity, no Authenticator wiring;
  the recipe explicitly covers what an Authenticator integration
  would add.

- recipes/shared-notepad/ — BasicAuth + per-user state map keyed by
  ctx.UserID() + explicit ctx.BroadcastAction("Refresh", nil) for
  peer-tab refresh. The Refresh action replaces the auto-Sync method
  removed in livetemplate#406; recipe text walks the migration.

Handler shape for shared-notepad ships two authenticator flavours:
NewDemoBasicAuth (what the e2e suite + examples/shared-notepad use)
and a documented Anonymous fallback for cmd/site. The substitution
is necessary because tinkerdown's embed-lvt does a server-side
prefetch to extract the LiveTemplate wrapper, and that prefetch
can't forward Authorization headers — a BasicAuth mount would
degrade to "live demo unavailable" in the embed. AnonymousAuthenticator
gives each browser its own cookie-bound session group, so the
multi-tab BroadcastAction demo works in-page; cross-identity
isolation surfaces by opening a private window. The recipe text
spells out the trade and what changes between flavours.

cmd/site mounts both at /apps/<slug>/ with the shared allowedOrigins
allowlist; tinkerdown.yaml gets two nav entries under Apps after
Todos. New e2e/login/ and e2e/shared-notepad/ packages match the
progressive-enhancement test scaffolding (cmd.WaitDelay-bounded
reap, bytes.Buffer stdout capture, two-consecutive-success
readiness probe). TestSharedNotepad_MultiUserIsolation is new
pedagogy beyond the original single-user example: HTTP-only, two
clients, asserts neither sees the other's text under either
authenticator.

Backlog state after this lands: zero must-do migrations remain.
Special landing-demo cross-origin discussion + final gh repo
archive livetemplate/examples are the only leave-in-place items left.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant