From 351c15d28e0444fd8a78c510a0c4d62ed433c758 Mon Sep 17 00:00:00 2001
From: Thomas Voss <mail@thomasvoss.com>
Date: Wed, 7 Aug 2024 00:21:12 +0200
Subject: Genesis commit

---
 vendor/github.com/a-h/templ/.dockerignore         |   3 +
 vendor/github.com/a-h/templ/.gitignore            |  28 +
 vendor/github.com/a-h/templ/.goreleaser.yaml      |  72 ++
 vendor/github.com/a-h/templ/.ignore               |   7 +
 vendor/github.com/a-h/templ/.version              |   1 +
 vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md    | 128 ++++
 vendor/github.com/a-h/templ/CONTRIBUTING.md       | 244 ++++++
 vendor/github.com/a-h/templ/LICENSE               |  21 +
 vendor/github.com/a-h/templ/README.md             | 171 +++++
 vendor/github.com/a-h/templ/SECURITY.md           |   9 +
 vendor/github.com/a-h/templ/cosign.pub            |   4 +
 vendor/github.com/a-h/templ/flake.lock            | 140 ++++
 vendor/github.com/a-h/templ/flake.nix             |  93 +++
 vendor/github.com/a-h/templ/flush.go              |  36 +
 vendor/github.com/a-h/templ/gomod2nix.toml        |  90 +++
 vendor/github.com/a-h/templ/handler.go            | 102 +++
 vendor/github.com/a-h/templ/ide-demo.gif          | Bin 0 -> 544148 bytes
 vendor/github.com/a-h/templ/jsonscript.go         |  85 +++
 vendor/github.com/a-h/templ/jsonstring.go         |  14 +
 vendor/github.com/a-h/templ/once.go               |  64 ++
 vendor/github.com/a-h/templ/push-tag.sh           |  14 +
 vendor/github.com/a-h/templ/runtime.go            | 855 ++++++++++++++++++++++
 vendor/github.com/a-h/templ/runtime/buffer.go     |  62 ++
 vendor/github.com/a-h/templ/runtime/bufferpool.go |  38 +
 vendor/github.com/a-h/templ/runtime/builder.go    |   8 +
 vendor/github.com/a-h/templ/runtime/runtime.go    |  21 +
 vendor/github.com/a-h/templ/safehtml/style.go     | 168 +++++
 vendor/github.com/a-h/templ/templ.png             | Bin 0 -> 15528 bytes
 vendor/github.com/a-h/templ/url.go                |  20 +
 vendor/github.com/a-h/templ/version.go            |  10 +
 30 files changed, 2508 insertions(+)
 create mode 100644 vendor/github.com/a-h/templ/.dockerignore
 create mode 100644 vendor/github.com/a-h/templ/.gitignore
 create mode 100644 vendor/github.com/a-h/templ/.goreleaser.yaml
 create mode 100644 vendor/github.com/a-h/templ/.ignore
 create mode 100644 vendor/github.com/a-h/templ/.version
 create mode 100644 vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md
 create mode 100644 vendor/github.com/a-h/templ/CONTRIBUTING.md
 create mode 100644 vendor/github.com/a-h/templ/LICENSE
 create mode 100644 vendor/github.com/a-h/templ/README.md
 create mode 100644 vendor/github.com/a-h/templ/SECURITY.md
 create mode 100644 vendor/github.com/a-h/templ/cosign.pub
 create mode 100644 vendor/github.com/a-h/templ/flake.lock
 create mode 100644 vendor/github.com/a-h/templ/flake.nix
 create mode 100644 vendor/github.com/a-h/templ/flush.go
 create mode 100644 vendor/github.com/a-h/templ/gomod2nix.toml
 create mode 100644 vendor/github.com/a-h/templ/handler.go
 create mode 100644 vendor/github.com/a-h/templ/ide-demo.gif
 create mode 100644 vendor/github.com/a-h/templ/jsonscript.go
 create mode 100644 vendor/github.com/a-h/templ/jsonstring.go
 create mode 100644 vendor/github.com/a-h/templ/once.go
 create mode 100644 vendor/github.com/a-h/templ/push-tag.sh
 create mode 100644 vendor/github.com/a-h/templ/runtime.go
 create mode 100644 vendor/github.com/a-h/templ/runtime/buffer.go
 create mode 100644 vendor/github.com/a-h/templ/runtime/bufferpool.go
 create mode 100644 vendor/github.com/a-h/templ/runtime/builder.go
 create mode 100644 vendor/github.com/a-h/templ/runtime/runtime.go
 create mode 100644 vendor/github.com/a-h/templ/safehtml/style.go
 create mode 100644 vendor/github.com/a-h/templ/templ.png
 create mode 100644 vendor/github.com/a-h/templ/url.go
 create mode 100644 vendor/github.com/a-h/templ/version.go

(limited to 'vendor/github.com/a-h')

diff --git a/vendor/github.com/a-h/templ/.dockerignore b/vendor/github.com/a-h/templ/.dockerignore
new file mode 100644
index 0000000..17896fe
--- /dev/null
+++ b/vendor/github.com/a-h/templ/.dockerignore
@@ -0,0 +1,3 @@
+.git
+Dockerfile
+.dockerignore
diff --git a/vendor/github.com/a-h/templ/.gitignore b/vendor/github.com/a-h/templ/.gitignore
new file mode 100644
index 0000000..0338eda
--- /dev/null
+++ b/vendor/github.com/a-h/templ/.gitignore
@@ -0,0 +1,28 @@
+# Output.
+cmd/templ/templ
+
+# Logs.
+cmd/templ/lspcmd/*log.txt
+
+# Go code coverage.
+coverage.out
+coverage
+
+# Mac filesystem jank.
+.DS_Store
+
+# Docusaurus.
+docs/build/
+docs/resources/_gen/
+node_modules/
+dist/
+
+# Nix artifacts.
+result
+
+# Editors
+## nvim
+.null-ls*
+
+# Go workspace.
+go.work
diff --git a/vendor/github.com/a-h/templ/.goreleaser.yaml b/vendor/github.com/a-h/templ/.goreleaser.yaml
new file mode 100644
index 0000000..456187c
--- /dev/null
+++ b/vendor/github.com/a-h/templ/.goreleaser.yaml
@@ -0,0 +1,72 @@
+builds:
+  - env:
+      - CGO_ENABLED=0
+    dir: cmd/templ
+    mod_timestamp: '{{ .CommitTimestamp }}'
+    flags:
+      - -trimpath
+    ldflags:
+      - -s -w
+    goos:
+      - linux
+      - windows
+      - darwin
+
+checksum:
+  name_template: 'checksums.txt'
+
+signs:
+  - id: checksums
+    cmd: cosign
+    stdin: '{{ .Env.COSIGN_PASSWORD }}'
+    output: true
+    artifacts: checksum
+    args:
+      - sign-blob
+      - --yes
+      - --key
+      - env://COSIGN_PRIVATE_KEY
+      - '--output-certificate=${certificate}'
+      - '--output-signature=${signature}'
+      - '${artifact}'
+
+archives:
+  - format: tar.gz
+    name_template: >-
+      {{ .ProjectName }}_
+      {{- title .Os }}_
+      {{- if eq .Arch "amd64" }}x86_64
+      {{- else if eq .Arch "386" }}i386
+      {{- else }}{{ .Arch }}{{ end }}
+      {{- if .Arm }}v{{ .Arm }}{{ end }}
+
+kos:
+  - repository: ghcr.io/a-h/templ
+    platforms:
+    - linux/amd64
+    - linux/arm64
+    tags:
+    - latest
+    - '{{.Tag}}'
+    bare: true
+
+docker_signs:
+  - cmd: cosign
+    artifacts: all
+    output: true
+    args:
+      - sign
+      - --yes
+      - --key
+      - env://COSIGN_PRIVATE_KEY
+      - '${artifact}'
+
+snapshot:
+  name_template: "{{ incpatch .Version }}-next"
+
+changelog:
+  sort: asc
+  filters:
+    exclude:
+      - '^docs:'
+      - '^test:'
diff --git a/vendor/github.com/a-h/templ/.ignore b/vendor/github.com/a-h/templ/.ignore
new file mode 100644
index 0000000..21cb25e
--- /dev/null
+++ b/vendor/github.com/a-h/templ/.ignore
@@ -0,0 +1,7 @@
+*_templ.go
+examples/integration-ct/static/index.js
+examples/counter/assets/css/bulma.*
+examples/counter/assets/js/htmx.min.js
+examples/counter-basic/assets/css/bulma.*
+examples/typescript/assets/index.js
+package-lock.json
diff --git a/vendor/github.com/a-h/templ/.version b/vendor/github.com/a-h/templ/.version
new file mode 100644
index 0000000..baee64f
--- /dev/null
+++ b/vendor/github.com/a-h/templ/.version
@@ -0,0 +1 @@
+0.2.747
\ No newline at end of file
diff --git a/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md b/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..08340d3
--- /dev/null
+++ b/vendor/github.com/a-h/templ/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+  and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+  overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+  advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+  address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+adrianhesketh@hushail.com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior,  harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/vendor/github.com/a-h/templ/CONTRIBUTING.md b/vendor/github.com/a-h/templ/CONTRIBUTING.md
new file mode 100644
index 0000000..e98d31f
--- /dev/null
+++ b/vendor/github.com/a-h/templ/CONTRIBUTING.md
@@ -0,0 +1,244 @@
+# Contributing to templ
+
+## Vision
+
+Enable Go developers to build strongly typed, component-based HTML user interfaces with first-class developer tooling, and a short learning curve.
+
+## Come up with a design and share it
+
+Before starting work on any major pull requests or code changes, start a discussion at https://github.com/a-h/templ/discussions or raise an issue.
+
+We don't want you to spend time on a PR or feature that ultimately doesn't get merged because it doesn't fit with the project goals, or the design doesn't work for some reason.
+
+For issues, it really helps if you provide a reproduction repo, or can create a failing unit test to describe the behaviour.
+
+In designs, we need to consider:
+
+* Backwards compatibility - Not changing the public API between releases, introducing gradual deprecation - don't break people's code.
+* Correctness over time - How can we reduce the risk of defects both now, and in future releases?
+* Threat model - How could each change be used to inject vulnerabilities into web pages?
+* Go version - We target the oldest supported version of Go as per https://go.dev/doc/devel/release
+* Automatic migration - If we need to force through a change.
+* Compile time vs runtime errors - Prefer compile time.
+* Documentation - New features are only useful if people can understand the new feature, what would the documentation look like?
+* Examples - How will we demonstrate the feature?
+
+## Project structure
+
+templ is structured into a few areas:
+
+### Parser `./parser`
+
+The parser directory currently contains both v1 and v2 parsers.
+
+The v1 parser is not maintained, it's only used to migrate v1 code over to the v2 syntax.
+
+The parser is responsible for parsing templ files into an object model. The types that make up the object model are in `types.go`. Automatic formatting of the types is tested in `types_test.go`.
+
+A templ file is parsed into the `TemplateFile` struct object model.
+
+```go
+type TemplateFile struct {
+	// Header contains comments or whitespace at the top of the file.
+	Header []GoExpression
+	// Package expression.
+	Package Package
+	// Nodes in the file.
+	Nodes []TemplateFileNode
+}
+```
+
+Parsers are individually tested using two types of unit test.
+
+One test covers the successful parsing of text into an object. For example, the `HTMLCommentParser` test checks for successful patterns.
+
+```go
+func TestHTMLCommentParser(t *testing.T) {
+	var tests = []struct {
+		name     string
+		input    string
+		expected HTMLComment
+	}{
+		{
+			name:  "comment - single line",
+			input: `<!-- single line comment -->`,
+			expected: HTMLComment{
+				Contents: " single line comment ",
+			},
+		},
+		{
+			name:  "comment - no whitespace",
+			input: `<!--no whitespace between sequence open and close-->`,
+			expected: HTMLComment{
+				Contents: "no whitespace between sequence open and close",
+			},
+		},
+		{
+			name: "comment - multiline",
+			input: `<!-- multiline
+								comment
+					-->`,
+			expected: HTMLComment{
+				Contents: ` multiline
+								comment
+					`,
+			},
+		},
+		{
+			name:  "comment - with tag",
+			input: `<!-- <p class="test">tag</p> -->`,
+			expected: HTMLComment{
+				Contents: ` <p class="test">tag</p> `,
+			},
+		},
+		{
+			name:  "comments can contain tags",
+			input: `<!-- <div> hello world </div> -->`,
+			expected: HTMLComment{
+				Contents: ` <div> hello world </div> `,
+			},
+		},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			input := parse.NewInput(tt.input)
+			result, ok, err := htmlComment.Parse(input)
+			if err != nil {
+				t.Fatalf("parser error: %v", err)
+			}
+			if !ok {
+				t.Fatalf("failed to parse at %d", input.Index())
+			}
+			if diff := cmp.Diff(tt.expected, result); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
+```
+
+Alongside each success test, is a similar test to check that invalid syntax is detected.
+
+```go
+func TestHTMLCommentParserErrors(t *testing.T) {
+	var tests = []struct {
+		name     string
+		input    string
+		expected error
+	}{
+		{
+			name:  "unclosed HTML comment",
+			input: `<!-- unclosed HTML comment`,
+			expected: parse.Error("expected end comment literal '-->' not found",
+				parse.Position{
+					Index: 26,
+					Line:  0,
+					Col:   26,
+				}),
+		},
+		{
+			name:  "comment in comment",
+			input: `<!-- <-- other --> -->`,
+			expected: parse.Error("comment contains invalid sequence '--'", parse.Position{
+				Index: 8,
+				Line:  0,
+				Col:   8,
+			}),
+		},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			input := parse.NewInput(tt.input)
+			_, _, err := htmlComment.Parse(input)
+			if diff := cmp.Diff(tt.expected, err); diff != "" {
+				t.Error(diff)
+			}
+		})
+	}
+}
+```
+
+### Generator
+
+The generator takes the object model and writes out Go code that produces the expected output. Any changes to Go code output by templ are made in this area.
+
+Testing of the generator is carried out by creating a templ file, and a matching expected output file.
+
+For example, `./generator/test-a-href` contains a templ file of:
+
+```templ
+package testahref
+
+templ render() {
+	<a href="javascript:alert(&#39;unaffected&#39;);">Ignored</a>
+	<a href={ templ.URL("javascript:alert('should be sanitized')") }>Sanitized</a>
+	<a href={ templ.SafeURL("javascript:alert('should not be sanitized')") }>Unsanitized</a>
+}
+```
+
+It also contains an expected output file.
+
+```html
+<a href="javascript:alert(&#39;unaffected&#39;);">Ignored</a>
+<a href="about:invalid#TemplFailedSanitizationURL">Sanitized</a>
+<a href="javascript:alert(&#39;should not be sanitized&#39;)">Unsanitized</a>
+```
+
+These tests contribute towards the code coverage metrics by building an instrumented test CLI program. See the `test-cover` task in the `README.md` file.
+
+### CLI
+
+The command line interface for templ is used to generate Go code from templ files, format templ files, and run the LSP.
+
+The code for this is at `./cmd/templ`.
+
+Testing of the templ command line is done with unit tests to check the argument parsing.
+
+The `templ generate` command is tested by generating templ files in the project, and testing that the expected output HTML is present.
+
+### Runtime
+
+The runtime is used by generated code, and by template authors, to serve template content over HTTP, and to carry out various operations.
+
+It is in the root directory of the project at `./runtime.go`. The runtime is unit tested, as well as being tested as part of the `generate` tests.
+
+### LSP
+
+The LSP is structured within the command line interface, and proxies commands through to the `gopls` LSP.
+
+### Docs
+
+The docs are a Docusaurus project at `./docs`.
+
+## Coding
+
+### Build tasks
+
+templ uses the `xc` task runner - https://github.com/joerdav/xc
+
+If you run `xc` you can get see a list of the development tasks that can be run, or you can read the `README.md` file and see the `Tasks` section.
+
+The most useful tasks for local development are:
+
+* `install-snapshot` - this builds the templ CLI and installs it into `~/bin`. Ensure that this is in your path.
+* `test` - this regenerates all templates, and runs the unit tests.
+* `fmt` - run the `gofmt` tool to format all Go code.
+* `lint` - run the same linting as run in the CI process.
+* `docs-run` - run the Docusaurus documentation site.
+
+### Commit messages
+
+The project using https://www.conventionalcommits.org/en/v1.0.0/
+
+Examples:
+
+* `feat: support Go comments in templates, fixes #234"`
+
+### Coding style
+
+* Reduce nesting - i.e. prefer early returns over an `else` block, as per https://danp.net/posts/reducing-go-nesting/ or https://go.dev/doc/effective_go#if
+* Use line breaks to separate "paragraphs" of code - don't use line breaks in between lines, or at the start/end of functions etc.
+* Use the `fmt` and `lint` build tasks to format and lint your code before submitting a PR.
+
diff --git a/vendor/github.com/a-h/templ/LICENSE b/vendor/github.com/a-h/templ/LICENSE
new file mode 100644
index 0000000..15e6fb8
--- /dev/null
+++ b/vendor/github.com/a-h/templ/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Adrian Hesketh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/a-h/templ/README.md b/vendor/github.com/a-h/templ/README.md
new file mode 100644
index 0000000..e3087f0
--- /dev/null
+++ b/vendor/github.com/a-h/templ/README.md
@@ -0,0 +1,171 @@
+![templ](https://github.com/a-h/templ/raw/main/templ.png)
+
+## An HTML templating language for Go that has great developer tooling.
+
+![templ](ide-demo.gif)
+
+
+## Documentation
+
+See user documentation at https://templ.guide
+
+<p align="center">
+<a href="https://pkg.go.dev/github.com/a-h/templ"><img src="https://pkg.go.dev/badge/github.com/a-h/templ.svg" alt="Go Reference" /></a>
+<a href="https://xcfile.dev"><img src="https://xcfile.dev/badge.svg" alt="xc compatible" /></a>
+<a href="https://raw.githack.com/wiki/a-h/templ/coverage.html"><img src="https://github.com/a-h/templ/wiki/coverage.svg" alt="Go Coverage" /></a>
+<a href="https://goreportcard.com/report/github.com/a-h/templ"><img src="https://goreportcard.com/badge/github.com/a-h/templ" alt="Go Report Card" /></a<
+</p>
+
+## Tasks
+
+### build
+
+Build a local version.
+
+```sh
+go run ./get-version > .version
+cd cmd/templ
+go build
+```
+
+### nix-update-gomod2nix
+
+```sh
+gomod2nix
+```
+
+### install-snapshot
+
+Build and install current version.
+
+```sh
+# Remove templ from the non-standard ~/bin/templ path
+# that this command previously used.
+rm -f ~/bin/templ
+# Clear LSP logs.
+rm -f cmd/templ/lspcmd/*.txt
+# Update version.
+go run ./get-version > .version
+# Install to $GOPATH/bin or $HOME/go/bin
+cd cmd/templ && go install
+```
+
+### build-snapshot
+
+Use goreleaser to build the command line binary using goreleaser.
+
+```sh
+goreleaser build --snapshot --clean
+```
+
+### generate
+
+Run templ generate using local version.
+
+```sh
+go run ./cmd/templ generate -include-version=false
+```
+
+### test
+
+Run Go tests.
+
+```sh
+go run ./get-version > .version
+go run ./cmd/templ generate -include-version=false
+go test ./...
+```
+
+### test-short
+
+Run Go tests.
+
+```sh
+go run ./get-version > .version
+go run ./cmd/templ generate -include-version=false
+go test ./... -short
+```
+
+### test-cover
+
+Run Go tests.
+
+```sh
+# Create test profile directories.
+mkdir -p coverage/fmt
+mkdir -p coverage/generate
+mkdir -p coverage/version
+mkdir -p coverage/unit
+# Build the test binary.
+go build -cover -o ./coverage/templ-cover ./cmd/templ
+# Run the covered generate command.
+GOCOVERDIR=coverage/fmt ./coverage/templ-cover fmt .
+GOCOVERDIR=coverage/generate ./coverage/templ-cover generate -include-version=false
+GOCOVERDIR=coverage/version ./coverage/templ-cover version
+# Run the unit tests.
+go test -cover ./... -coverpkg ./... -args -test.gocoverdir="$PWD/coverage/unit"
+# Display the combined percentage.
+go tool covdata percent -i=./coverage/fmt,./coverage/generate,./coverage/version,./coverage/unit
+# Generate a text coverage profile for tooling to use.
+go tool covdata textfmt -i=./coverage/fmt,./coverage/generate,./coverage/version,./coverage/unit -o coverage.out
+# Print total
+go tool cover -func coverage.out | grep total
+```
+
+### test-cover-watch
+
+```sh
+gotestsum --watch -- -coverprofile=coverage.out
+```
+
+### benchmark
+
+Run benchmarks.
+
+```sh
+go run ./cmd/templ generate -include-version=false && go test ./... -bench=. -benchmem
+```
+
+### fmt
+
+Format all Go and templ code.
+
+```sh
+gofmt -s -w .
+go run ./cmd/templ fmt .
+```
+
+### lint
+
+```sh
+golangci-lint run --verbose
+```
+
+### push-release-tag
+
+Push a semantic version number to Github to trigger the release process.
+
+```sh
+./push-tag.sh
+```
+
+### docs-run
+
+Run the development server.
+
+Directory: docs
+
+```sh
+npm run start
+```
+
+### docs-build
+
+Build production docs site.
+
+Directory: docs
+
+```sh
+npm run build
+```
+
diff --git a/vendor/github.com/a-h/templ/SECURITY.md b/vendor/github.com/a-h/templ/SECURITY.md
new file mode 100644
index 0000000..8241f55
--- /dev/null
+++ b/vendor/github.com/a-h/templ/SECURITY.md
@@ -0,0 +1,9 @@
+# Security Policy
+
+## Supported Versions
+
+The latest version of templ is supported.
+
+## Reporting a Vulnerability
+
+Use the "Security" tab in Github and fill out the "Report a vulnerability" form.
diff --git a/vendor/github.com/a-h/templ/cosign.pub b/vendor/github.com/a-h/templ/cosign.pub
new file mode 100644
index 0000000..9d7967b
--- /dev/null
+++ b/vendor/github.com/a-h/templ/cosign.pub
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqHp75uAj8XqKrLO2YvY0M2EddckH
+evQnNAj+0GmBptqdf3NJcUCjL6w4z2Ikh/Zb8lh6b13akAwO/dJQaMLoMA==
+-----END PUBLIC KEY-----
diff --git a/vendor/github.com/a-h/templ/flake.lock b/vendor/github.com/a-h/templ/flake.lock
new file mode 100644
index 0000000..af4e370
--- /dev/null
+++ b/vendor/github.com/a-h/templ/flake.lock
@@ -0,0 +1,140 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1694529238,
+        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_2": {
+      "locked": {
+        "lastModified": 1667395993,
+        "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709087332,
+        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "gomod2nix": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1717050755,
+        "narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=",
+        "owner": "nix-community",
+        "repo": "gomod2nix",
+        "rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "gomod2nix",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1720096762,
+        "narHash": "sha256-KvpJIWxTNuaSpN2L/9TmTlEhlwxEnzJ1vCpEcfK/4mQ=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "638369f687471823770f6d3093f1721dc7b8c897",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "release-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "gitignore": "gitignore",
+        "gomod2nix": "gomod2nix",
+        "nixpkgs": "nixpkgs",
+        "xc": "xc"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "xc": {
+      "inputs": {
+        "flake-utils": "flake-utils_2",
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1717601811,
+        "narHash": "sha256-+XQvDRXpzjBdZI3JGKP6SAOYXM+JSEbWL5kqtCwRJXE=",
+        "owner": "joerdav",
+        "repo": "xc",
+        "rev": "f8e8e658978d6c9fe49c27b684ca7375a74deef1",
+        "type": "github"
+      },
+      "original": {
+        "owner": "joerdav",
+        "repo": "xc",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/vendor/github.com/a-h/templ/flake.nix b/vendor/github.com/a-h/templ/flake.nix
new file mode 100644
index 0000000..fd8a238
--- /dev/null
+++ b/vendor/github.com/a-h/templ/flake.nix
@@ -0,0 +1,93 @@
+{
+  description = "templ";
+
+  inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs/release-24.05";
+    gomod2nix = {
+      url = "github:nix-community/gomod2nix";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+    gitignore = {
+      url = "github:hercules-ci/gitignore.nix";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+    xc = {
+      url = "github:joerdav/xc";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+  };
+
+  outputs = { self, nixpkgs, gomod2nix, gitignore, xc }:
+    let
+      allSystems = [
+        "x86_64-linux" # 64-bit Intel/AMD Linux
+        "aarch64-linux" # 64-bit ARM Linux
+        "x86_64-darwin" # 64-bit Intel macOS
+        "aarch64-darwin" # 64-bit ARM macOS
+      ];
+      forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
+        inherit system;
+        pkgs = import nixpkgs { inherit system; };
+      });
+    in
+    {
+      packages = forAllSystems ({ system, pkgs, ... }:
+        let
+          buildGoApplication = gomod2nix.legacyPackages.${system}.buildGoApplication;
+        in
+        rec {
+          default = templ;
+
+          templ = buildGoApplication {
+            name = "templ";
+            src = gitignore.lib.gitignoreSource ./.;
+            # Update to latest Go version when https://nixpk.gs/pr-tracker.html?pr=324123 is backported to release-24.05.
+            go = pkgs.go;
+            # Must be added due to bug https://github.com/nix-community/gomod2nix/issues/120
+            pwd = ./.;
+            subPackages = [ "cmd/templ" ];
+            CGO_ENABLED = 0;
+            flags = [
+              "-trimpath"
+            ];
+            ldflags = [
+              "-s"
+              "-w"
+              "-extldflags -static"
+            ];
+          };
+        });
+
+      # `nix develop` provides a shell containing development tools.
+      devShell = forAllSystems ({ system, pkgs }:
+        pkgs.mkShell {
+          buildInputs = with pkgs; [
+            (golangci-lint.override { buildGoModule = buildGo121Module; })
+            cosign # Used to sign container images.
+            esbuild # Used to package JS examples.
+            go_1_21
+            gomod2nix.legacyPackages.${system}.gomod2nix
+            gopls
+            goreleaser
+            gotestsum
+            ko # Used to build Docker images.
+            nodejs # Used to build templ-docs.
+            xc.packages.${system}.xc
+          ];
+        });
+
+      # This flake outputs an overlay that can be used to add templ and
+      # templ-docs to nixpkgs as per https://templ.guide/quick-start/installation/#nix
+      #
+      # Example usage:
+      #
+      # nixpkgs.overlays = [
+      #   inputs.templ.overlays.default
+      # ];
+      overlays.default = final: prev: {
+        templ = self.packages.${final.stdenv.system}.templ;
+        templ-docs = self.packages.${final.stdenv.system}.templ-docs;
+      };
+    };
+}
+
diff --git a/vendor/github.com/a-h/templ/flush.go b/vendor/github.com/a-h/templ/flush.go
new file mode 100644
index 0000000..56d7d3a
--- /dev/null
+++ b/vendor/github.com/a-h/templ/flush.go
@@ -0,0 +1,36 @@
+package templ
+
+import (
+	"context"
+	"io"
+)
+
+// Flush flushes the output buffer after all its child components have been rendered.
+func Flush() FlushComponent {
+	return FlushComponent{}
+}
+
+type FlushComponent struct {
+}
+
+type flusherError interface {
+	Flush() error
+}
+
+type flusher interface {
+	Flush()
+}
+
+func (f FlushComponent) Render(ctx context.Context, w io.Writer) (err error) {
+	if err = GetChildren(ctx).Render(ctx, w); err != nil {
+		return err
+	}
+	switch w := w.(type) {
+	case flusher:
+		w.Flush()
+		return nil
+	case flusherError:
+		return w.Flush()
+	}
+	return nil
+}
diff --git a/vendor/github.com/a-h/templ/gomod2nix.toml b/vendor/github.com/a-h/templ/gomod2nix.toml
new file mode 100644
index 0000000..d572a5f
--- /dev/null
+++ b/vendor/github.com/a-h/templ/gomod2nix.toml
@@ -0,0 +1,90 @@
+schema = 3
+
+[mod]
+  [mod."github.com/PuerkitoBio/goquery"]
+    version = "v1.8.1"
+    hash = "sha256-z2RaB8PVPEzSJdMUfkfNjT616yXWTjW2gkhNOh989ZU="
+  [mod."github.com/a-h/htmlformat"]
+    version = "v0.0.0-20231108124658-5bd994fe268e"
+    hash = "sha256-YSl9GsXhc0L2oKGZLwwjUtpe5W6ra6kk74zvQdsDCMU="
+  [mod."github.com/a-h/parse"]
+    version = "v0.0.0-20240121214402-3caf7543159a"
+    hash = "sha256-ee/g6xwwhtF7vVt3griUSh96Kz4z0hM5/tpXxHW6PZk="
+  [mod."github.com/a-h/pathvars"]
+    version = "v0.0.14"
+    hash = "sha256-2NytUpcO0zbzE5XunCLcK3jDqxYzmyb3WqtYDEudAYg="
+  [mod."github.com/a-h/protocol"]
+    version = "v0.0.0-20240704131721-1e461c188041"
+    hash = "sha256-KSw8m+kVIubEi+nuS3dMdBw2ZZTlmcKD/hGbVRFaE5Q="
+  [mod."github.com/andybalholm/brotli"]
+    version = "v1.1.0"
+    hash = "sha256-njLViV4v++ZdgOWGWzlvkefuFvA/nkugl3Ta/h1nu/0="
+  [mod."github.com/andybalholm/cascadia"]
+    version = "v1.3.1"
+    hash = "sha256-M0u22DXSeXUaYtl1KoW1qWL46niFpycFkraCEQ/luYA="
+  [mod."github.com/cenkalti/backoff/v4"]
+    version = "v4.3.0"
+    hash = "sha256-wfVjNZsGG1WoNC5aL+kdcy6QXPgZo4THAevZ1787md8="
+  [mod."github.com/cli/browser"]
+    version = "v1.3.0"
+    hash = "sha256-06hcvQeOEm31clxkTuZ8ts8ZtdNKY575EsM1osRVpLg="
+  [mod."github.com/fatih/color"]
+    version = "v1.16.0"
+    hash = "sha256-Aq/SM28aPJVzvapllQ64R/DM4aZ5CHPewcm/AUJPyJQ="
+  [mod."github.com/fsnotify/fsnotify"]
+    version = "v1.7.0"
+    hash = "sha256-MdT2rQyQHspPJcx6n9ozkLbsktIOJutOqDuKpNAtoZY="
+  [mod."github.com/google/go-cmp"]
+    version = "v0.6.0"
+    hash = "sha256-qgra5jze4iPGP0JSTVeY5qV5AvEnEu39LYAuUCIkMtg="
+  [mod."github.com/mattn/go-colorable"]
+    version = "v0.1.13"
+    hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8="
+  [mod."github.com/mattn/go-isatty"]
+    version = "v0.0.20"
+    hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
+  [mod."github.com/natefinch/atomic"]
+    version = "v1.0.1"
+    hash = "sha256-fbOVHCwRNI8PFjC4o0YXpKZO0JU2aWTfH5c7WXXKMHg="
+  [mod."github.com/rs/cors"]
+    version = "v1.11.0"
+    hash = "sha256-hF25bVehtWCQsxiOfLuL4Hv8NKVunEqLPk/Vcuheha0="
+  [mod."github.com/segmentio/asm"]
+    version = "v1.2.0"
+    hash = "sha256-zbNuKxNrUDUc6IlmRQNuJQzVe5Ol/mqp7srDg9IMMqs="
+  [mod."github.com/segmentio/encoding"]
+    version = "v0.4.0"
+    hash = "sha256-4pWI9eTZRRDP9kO8rG6vbLCtBVVRLtbCJKd0Z2+8JoU="
+  [mod."github.com/stretchr/testify"]
+    version = "v1.8.4"
+    hash = "sha256-MoOmRzbz9QgiJ+OOBo5h5/LbilhJfRUryvzHJmXAWjo="
+  [mod."go.lsp.dev/jsonrpc2"]
+    version = "v0.10.0"
+    hash = "sha256-RbRsMYVBLR7ZDHHGMooycrkdbIauMXkQjVOGP7ggSgM="
+  [mod."go.lsp.dev/pkg"]
+    version = "v0.0.0-20210717090340-384b27a52fb2"
+    hash = "sha256-TxS0Iqe1wbIaFe7MWZJRQdgqhKE8i8CggaGSV9zU1Vg="
+  [mod."go.lsp.dev/uri"]
+    version = "v0.3.0"
+    hash = "sha256-jGP0N7Gf+bql5oJraUo33sXqWg7AKOTj0D8b4paV4dc="
+  [mod."go.uber.org/multierr"]
+    version = "v1.11.0"
+    hash = "sha256-Lb6rHHfR62Ozg2j2JZy3MKOMKdsfzd1IYTR57r3Mhp0="
+  [mod."go.uber.org/zap"]
+    version = "v1.27.0"
+    hash = "sha256-8655KDrulc4Das3VRduO9MjCn8ZYD5WkULjCvruaYsU="
+  [mod."golang.org/x/mod"]
+    version = "v0.17.0"
+    hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU="
+  [mod."golang.org/x/net"]
+    version = "v0.24.0"
+    hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ="
+  [mod."golang.org/x/sync"]
+    version = "v0.3.0"
+    hash = "sha256-bCJKLvwExhYacH2ZrWlZ38lr1d6oNenNt2m1QqDCs0o="
+  [mod."golang.org/x/sys"]
+    version = "v0.21.0"
+    hash = "sha256-gapzPWuEqY36V6W2YhIDYR49sEvjJRd7bSuf9K1f4JY="
+  [mod."golang.org/x/tools"]
+    version = "v0.13.0"
+    hash = "sha256-OCgLOwia8fNHxfdogXVApf0/qK6jE2ukegOx7lkOzfo="
diff --git a/vendor/github.com/a-h/templ/handler.go b/vendor/github.com/a-h/templ/handler.go
new file mode 100644
index 0000000..a28d561
--- /dev/null
+++ b/vendor/github.com/a-h/templ/handler.go
@@ -0,0 +1,102 @@
+package templ
+
+import "net/http"
+
+// ComponentHandler is a http.Handler that renders components.
+type ComponentHandler struct {
+	Component      Component
+	Status         int
+	ContentType    string
+	ErrorHandler   func(r *http.Request, err error) http.Handler
+	StreamResponse bool
+}
+
+const componentHandlerErrorMessage = "templ: failed to render template"
+
+func (ch *ComponentHandler) ServeHTTPBuffered(w http.ResponseWriter, r *http.Request) {
+	// Since the component may error, write to a buffer first.
+	// This prevents partial responses from being written to the client.
+	buf := GetBuffer()
+	defer ReleaseBuffer(buf)
+	err := ch.Component.Render(r.Context(), buf)
+	if err != nil {
+		if ch.ErrorHandler != nil {
+			w.Header().Set("Content-Type", ch.ContentType)
+			ch.ErrorHandler(r, err).ServeHTTP(w, r)
+			return
+		}
+		http.Error(w, componentHandlerErrorMessage, http.StatusInternalServerError)
+		return
+	}
+	w.Header().Set("Content-Type", ch.ContentType)
+	if ch.Status != 0 {
+		w.WriteHeader(ch.Status)
+	}
+	// Ignore write error like http.Error() does, because there is
+	// no way to recover at this point.
+	_, _ = w.Write(buf.Bytes())
+}
+
+func (ch *ComponentHandler) ServeHTTPStreamed(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", ch.ContentType)
+	if ch.Status != 0 {
+		w.WriteHeader(ch.Status)
+	}
+	if err := ch.Component.Render(r.Context(), w); err != nil {
+		if ch.ErrorHandler != nil {
+			w.Header().Set("Content-Type", ch.ContentType)
+			ch.ErrorHandler(r, err).ServeHTTP(w, r)
+			return
+		}
+		http.Error(w, componentHandlerErrorMessage, http.StatusInternalServerError)
+	}
+}
+
+// ServeHTTP implements the http.Handler interface.
+func (ch ComponentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if ch.StreamResponse {
+		ch.ServeHTTPStreamed(w, r)
+		return
+	}
+	ch.ServeHTTPBuffered(w, r)
+}
+
+// Handler creates a http.Handler that renders the template.
+func Handler(c Component, options ...func(*ComponentHandler)) *ComponentHandler {
+	ch := &ComponentHandler{
+		Component:   c,
+		ContentType: "text/html; charset=utf-8",
+	}
+	for _, o := range options {
+		o(ch)
+	}
+	return ch
+}
+
+// WithStatus sets the HTTP status code returned by the ComponentHandler.
+func WithStatus(status int) func(*ComponentHandler) {
+	return func(ch *ComponentHandler) {
+		ch.Status = status
+	}
+}
+
+// WithContentType sets the Content-Type header returned by the ComponentHandler.
+func WithContentType(contentType string) func(*ComponentHandler) {
+	return func(ch *ComponentHandler) {
+		ch.ContentType = contentType
+	}
+}
+
+// WithErrorHandler sets the error handler used if rendering fails.
+func WithErrorHandler(eh func(r *http.Request, err error) http.Handler) func(*ComponentHandler) {
+	return func(ch *ComponentHandler) {
+		ch.ErrorHandler = eh
+	}
+}
+
+// WithStreaming sets the ComponentHandler to stream the response instead of buffering it.
+func WithStreaming() func(*ComponentHandler) {
+	return func(ch *ComponentHandler) {
+		ch.StreamResponse = true
+	}
+}
diff --git a/vendor/github.com/a-h/templ/ide-demo.gif b/vendor/github.com/a-h/templ/ide-demo.gif
new file mode 100644
index 0000000..e35fd68
Binary files /dev/null and b/vendor/github.com/a-h/templ/ide-demo.gif differ
diff --git a/vendor/github.com/a-h/templ/jsonscript.go b/vendor/github.com/a-h/templ/jsonscript.go
new file mode 100644
index 0000000..6e88174
--- /dev/null
+++ b/vendor/github.com/a-h/templ/jsonscript.go
@@ -0,0 +1,85 @@
+package templ
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+)
+
+var _ Component = JSONScriptElement{}
+
+// JSONScript renders a JSON object inside a script element.
+// e.g. <script type="application/json">{"foo":"bar"}</script>
+func JSONScript(id string, data any) JSONScriptElement {
+	return JSONScriptElement{
+		ID:    id,
+		Type:  "application/json",
+		Data:  data,
+		Nonce: GetNonce,
+	}
+}
+
+// WithType sets the value of the type attribute of the script element.
+func (j JSONScriptElement) WithType(t string) JSONScriptElement {
+	j.Type = t
+	return j
+}
+
+// WithNonceFromString sets the value of the nonce attribute of the script element to the given string.
+func (j JSONScriptElement) WithNonceFromString(nonce string) JSONScriptElement {
+	j.Nonce = func(context.Context) string {
+		return nonce
+	}
+	return j
+}
+
+// WithNonceFrom sets the value of the nonce attribute of the script element to the value returned by the given function.
+func (j JSONScriptElement) WithNonceFrom(f func(context.Context) string) JSONScriptElement {
+	j.Nonce = f
+	return j
+}
+
+type JSONScriptElement struct {
+	// ID of the element in the DOM.
+	ID string
+	// Type of the script element, defaults to "application/json".
+	Type string
+	// Data that will be encoded as JSON.
+	Data any
+	// Nonce is a function that returns a CSP nonce.
+	// Defaults to CSPNonceFromContext.
+	// See https://content-security-policy.com/nonce for more information.
+	Nonce func(ctx context.Context) string
+}
+
+func (j JSONScriptElement) Render(ctx context.Context, w io.Writer) (err error) {
+	if _, err = io.WriteString(w, "<script"); err != nil {
+		return err
+	}
+	if j.ID != "" {
+		if _, err = fmt.Fprintf(w, " id=\"%s\"", EscapeString(j.ID)); err != nil {
+			return err
+		}
+	}
+	if j.Type != "" {
+		if _, err = fmt.Fprintf(w, " type=\"%s\"", EscapeString(j.Type)); err != nil {
+			return err
+		}
+	}
+	if nonce := j.Nonce(ctx); nonce != "" {
+		if _, err = fmt.Fprintf(w, " nonce=\"%s\"", EscapeString(nonce)); err != nil {
+			return err
+		}
+	}
+	if _, err = io.WriteString(w, ">"); err != nil {
+		return err
+	}
+	if err = json.NewEncoder(w).Encode(j.Data); err != nil {
+		return err
+	}
+	if _, err = io.WriteString(w, "</script>"); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/vendor/github.com/a-h/templ/jsonstring.go b/vendor/github.com/a-h/templ/jsonstring.go
new file mode 100644
index 0000000..425e4e8
--- /dev/null
+++ b/vendor/github.com/a-h/templ/jsonstring.go
@@ -0,0 +1,14 @@
+package templ
+
+import (
+	"encoding/json"
+)
+
+// JSONString returns a JSON encoded string of v.
+func JSONString(v any) (string, error) {
+	b, err := json.Marshal(v)
+	if err != nil {
+		return "", err
+	}
+	return string(b), nil
+}
diff --git a/vendor/github.com/a-h/templ/once.go b/vendor/github.com/a-h/templ/once.go
new file mode 100644
index 0000000..7860ab8
--- /dev/null
+++ b/vendor/github.com/a-h/templ/once.go
@@ -0,0 +1,64 @@
+package templ
+
+import (
+	"context"
+	"io"
+	"sync/atomic"
+)
+
+// onceHandleIndex is used to identify unique once handles in a program run.
+var onceHandleIndex int64
+
+type OnceOpt func(*OnceHandle)
+
+// WithOnceComponent sets the component to be rendered once per context.
+// This can be used instead of setting the children of the `Once` method,
+// for example, if creating a code component outside of a templ HTML template.
+func WithComponent(c Component) OnceOpt {
+	return func(o *OnceHandle) {
+		o.c = c
+	}
+}
+
+// NewOnceHandle creates a OnceHandle used to ensure that the children of its
+// `Once` method are only rendered once per context.
+func NewOnceHandle(opts ...OnceOpt) *OnceHandle {
+	oh := &OnceHandle{
+		id: atomic.AddInt64(&onceHandleIndex, 1),
+	}
+	for _, opt := range opts {
+		opt(oh)
+	}
+	return oh
+}
+
+// OnceHandle is used to ensure that the children of its `Once` method are are only
+// rendered once per context.
+type OnceHandle struct {
+	// id is used to identify which instance of the OnceHandle is being used.
+	// The OnceHandle can't be an empty struct, because:
+	//
+	//  | Two distinct zero-size variables may
+	//  | have the same address in memory
+	//
+	// https://go.dev/ref/spec#Size_and_alignment_guarantees
+	id int64
+	// c is the component to be rendered once per context.
+	// if c is nil, the children of the `Once` method are rendered.
+	c Component
+}
+
+// Once returns a component that renders its children once per context.
+func (o *OnceHandle) Once() Component {
+	return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+		_, v := getContext(ctx)
+		if v.getHasBeenRendered(o) {
+			return nil
+		}
+		v.setHasBeenRendered(o)
+		if o.c != nil {
+			return o.c.Render(ctx, w)
+		}
+		return GetChildren(ctx).Render(ctx, w)
+	})
+}
diff --git a/vendor/github.com/a-h/templ/push-tag.sh b/vendor/github.com/a-h/templ/push-tag.sh
new file mode 100644
index 0000000..9eedeed
--- /dev/null
+++ b/vendor/github.com/a-h/templ/push-tag.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+if [ `git rev-parse --abbrev-ref HEAD` != "main" ]; then
+  echo "Error: Not on main branch. Please switch to main branch.";
+  exit 1;
+fi
+git pull
+if ! git diff --quiet; then
+  echo "Error: Working directory is not clean. Please commit the changes first.";
+  exit 1;
+fi
+export VERSION=`cat .version`
+echo Adding git tag with version v${VERSION};
+git tag v${VERSION};
+git push origin v${VERSION};
diff --git a/vendor/github.com/a-h/templ/runtime.go b/vendor/github.com/a-h/templ/runtime.go
new file mode 100644
index 0000000..d4d5aa0
--- /dev/null
+++ b/vendor/github.com/a-h/templ/runtime.go
@@ -0,0 +1,855 @@
+package templ
+
+import (
+	"bytes"
+	"context"
+	"crypto/sha256"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"html"
+	"html/template"
+	"io"
+	"net/http"
+	"os"
+	"reflect"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/a-h/templ/safehtml"
+)
+
+// Types exposed by all components.
+
+// Component is the interface that all templates implement.
+type Component interface {
+	// Render the template.
+	Render(ctx context.Context, w io.Writer) error
+}
+
+// ComponentFunc converts a function that matches the Component interface's
+// Render method into a Component.
+type ComponentFunc func(ctx context.Context, w io.Writer) error
+
+// Render the template.
+func (cf ComponentFunc) Render(ctx context.Context, w io.Writer) error {
+	return cf(ctx, w)
+}
+
+// WithNonce sets a CSP nonce on the context and returns it.
+func WithNonce(ctx context.Context, nonce string) context.Context {
+	ctx, v := getContext(ctx)
+	v.nonce = nonce
+	return ctx
+}
+
+// GetNonce returns the CSP nonce value set with WithNonce, or an
+// empty string if none has been set.
+func GetNonce(ctx context.Context) (nonce string) {
+	if ctx == nil {
+		return ""
+	}
+	_, v := getContext(ctx)
+	return v.nonce
+}
+
+func WithChildren(ctx context.Context, children Component) context.Context {
+	ctx, v := getContext(ctx)
+	v.children = &children
+	return ctx
+}
+
+func ClearChildren(ctx context.Context) context.Context {
+	_, v := getContext(ctx)
+	v.children = nil
+	return ctx
+}
+
+// NopComponent is a component that doesn't render anything.
+var NopComponent = ComponentFunc(func(ctx context.Context, w io.Writer) error { return nil })
+
+// GetChildren from the context.
+func GetChildren(ctx context.Context) Component {
+	_, v := getContext(ctx)
+	if v.children == nil {
+		return NopComponent
+	}
+	return *v.children
+}
+
+// EscapeString escapes HTML text within templates.
+func EscapeString(s string) string {
+	return html.EscapeString(s)
+}
+
+// Bool attribute value.
+func Bool(value bool) bool {
+	return value
+}
+
+// Classes for CSS.
+// Supported types are string, ConstantCSSClass, ComponentCSSClass, map[string]bool.
+func Classes(classes ...any) CSSClasses {
+	return CSSClasses(classes)
+}
+
+// CSSClasses is a slice of CSS classes.
+type CSSClasses []any
+
+// String returns the names of all CSS classes.
+func (classes CSSClasses) String() string {
+	if len(classes) == 0 {
+		return ""
+	}
+	cp := newCSSProcessor()
+	for _, v := range classes {
+		cp.Add(v)
+	}
+	return cp.String()
+}
+
+func newCSSProcessor() *cssProcessor {
+	return &cssProcessor{
+		classNameToEnabled: make(map[string]bool),
+	}
+}
+
+type cssProcessor struct {
+	classNameToEnabled map[string]bool
+	orderedNames       []string
+}
+
+func (cp *cssProcessor) Add(item any) {
+	switch c := item.(type) {
+	case []string:
+		for _, className := range c {
+			cp.AddClassName(className, true)
+		}
+	case string:
+		cp.AddClassName(c, true)
+	case ConstantCSSClass:
+		cp.AddClassName(c.ClassName(), true)
+	case ComponentCSSClass:
+		cp.AddClassName(c.ClassName(), true)
+	case map[string]bool:
+		// In Go, map keys are iterated in a randomized order.
+		// So the keys in the map must be sorted to produce consistent output.
+		keys := make([]string, len(c))
+		var i int
+		for key := range c {
+			keys[i] = key
+			i++
+		}
+		sort.Strings(keys)
+		for _, className := range keys {
+			cp.AddClassName(className, c[className])
+		}
+	case []KeyValue[string, bool]:
+		for _, kv := range c {
+			cp.AddClassName(kv.Key, kv.Value)
+		}
+	case KeyValue[string, bool]:
+		cp.AddClassName(c.Key, c.Value)
+	case []KeyValue[CSSClass, bool]:
+		for _, kv := range c {
+			cp.AddClassName(kv.Key.ClassName(), kv.Value)
+		}
+	case KeyValue[CSSClass, bool]:
+		cp.AddClassName(c.Key.ClassName(), c.Value)
+	case CSSClasses:
+		for _, item := range c {
+			cp.Add(item)
+		}
+	case []CSSClass:
+		for _, item := range c {
+			cp.Add(item)
+		}
+	case func() CSSClass:
+		cp.AddClassName(c().ClassName(), true)
+	default:
+		cp.AddClassName(unknownTypeClassName, true)
+	}
+}
+
+func (cp *cssProcessor) AddClassName(className string, enabled bool) {
+	cp.classNameToEnabled[className] = enabled
+	cp.orderedNames = append(cp.orderedNames, className)
+}
+
+func (cp *cssProcessor) String() string {
+	// Order the outputs according to how they were input, and remove disabled names.
+	rendered := make(map[string]any, len(cp.classNameToEnabled))
+	var names []string
+	for _, name := range cp.orderedNames {
+		if enabled := cp.classNameToEnabled[name]; !enabled {
+			continue
+		}
+		if _, hasBeenRendered := rendered[name]; hasBeenRendered {
+			continue
+		}
+		names = append(names, name)
+		rendered[name] = struct{}{}
+	}
+
+	return strings.Join(names, " ")
+}
+
+// KeyValue is a key and value pair.
+type KeyValue[TKey comparable, TValue any] struct {
+	Key   TKey   `json:"name"`
+	Value TValue `json:"value"`
+}
+
+// KV creates a new key/value pair from the input key and value.
+func KV[TKey comparable, TValue any](key TKey, value TValue) KeyValue[TKey, TValue] {
+	return KeyValue[TKey, TValue]{
+		Key:   key,
+		Value: value,
+	}
+}
+
+const unknownTypeClassName = "--templ-css-class-unknown-type"
+
+// Class returns a CSS class name.
+// Deprecated: use a string instead.
+func Class(name string) CSSClass {
+	return SafeClass(name)
+}
+
+// SafeClass bypasses CSS class name validation.
+// Deprecated: use a string instead.
+func SafeClass(name string) CSSClass {
+	return ConstantCSSClass(name)
+}
+
+// CSSClass provides a class name.
+type CSSClass interface {
+	ClassName() string
+}
+
+// ConstantCSSClass is a string constant of a CSS class name.
+// Deprecated: use a string instead.
+type ConstantCSSClass string
+
+// ClassName of the CSS class.
+func (css ConstantCSSClass) ClassName() string {
+	return string(css)
+}
+
+// ComponentCSSClass is a templ.CSS
+type ComponentCSSClass struct {
+	// ID of the class, will be autogenerated.
+	ID string
+	// Definition of the CSS.
+	Class SafeCSS
+}
+
+// ClassName of the CSS class.
+func (css ComponentCSSClass) ClassName() string {
+	return css.ID
+}
+
+// CSSID calculates an ID.
+func CSSID(name string, css string) string {
+	sum := sha256.Sum256([]byte(css))
+	hp := hex.EncodeToString(sum[:])[0:4]
+	// Benchmarking showed this was fastest, and with fewest allocations (1).
+	// Using strings.Builder (2 allocs).
+	// Using fmt.Sprintf (3 allocs).
+	return name + "_" + hp
+}
+
+// NewCSSMiddleware creates HTTP middleware that renders a global stylesheet of ComponentCSSClass
+// CSS if the request path matches, or updates the HTTP context to ensure that any handlers that
+// use templ.Components skip rendering <style> elements for classes that are included in the global
+// stylesheet. By default, the stylesheet path is /styles/templ.css
+func NewCSSMiddleware(next http.Handler, classes ...CSSClass) CSSMiddleware {
+	return CSSMiddleware{
+		Path:       "/styles/templ.css",
+		CSSHandler: NewCSSHandler(classes...),
+		Next:       next,
+	}
+}
+
+// CSSMiddleware renders a global stylesheet.
+type CSSMiddleware struct {
+	Path       string
+	CSSHandler CSSHandler
+	Next       http.Handler
+}
+
+func (cssm CSSMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if r.URL.Path == cssm.Path {
+		cssm.CSSHandler.ServeHTTP(w, r)
+		return
+	}
+	// Add registered classes to the context.
+	ctx, v := getContext(r.Context())
+	for _, c := range cssm.CSSHandler.Classes {
+		v.addClass(c.ID)
+	}
+	// Serve the request. Templ components will use the updated context
+	// to know to skip rendering <style> elements for any component CSS
+	// classes that have been included in the global stylesheet.
+	cssm.Next.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// NewCSSHandler creates a handler that serves a stylesheet containing the CSS of the
+// classes passed in. This is used by the CSSMiddleware to provide global stylesheets
+// for templ components.
+func NewCSSHandler(classes ...CSSClass) CSSHandler {
+	ccssc := make([]ComponentCSSClass, 0, len(classes))
+	for _, c := range classes {
+		ccss, ok := c.(ComponentCSSClass)
+		if !ok {
+			continue
+		}
+		ccssc = append(ccssc, ccss)
+	}
+	return CSSHandler{
+		Classes: ccssc,
+	}
+}
+
+// CSSHandler is a HTTP handler that serves CSS.
+type CSSHandler struct {
+	Logger  func(err error)
+	Classes []ComponentCSSClass
+}
+
+func (cssh CSSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "text/css")
+	for _, c := range cssh.Classes {
+		_, err := w.Write([]byte(c.Class))
+		if err != nil && cssh.Logger != nil {
+			cssh.Logger(err)
+		}
+	}
+}
+
+// RenderCSSItems renders the CSS to the writer, if the items haven't already been rendered.
+func RenderCSSItems(ctx context.Context, w io.Writer, classes ...any) (err error) {
+	if len(classes) == 0 {
+		return nil
+	}
+	_, v := getContext(ctx)
+	sb := new(strings.Builder)
+	renderCSSItemsToBuilder(sb, v, classes...)
+	if sb.Len() > 0 {
+		if _, err = io.WriteString(w, `<style type="text/css">`); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, sb.String()); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, `</style>`); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func renderCSSItemsToBuilder(sb *strings.Builder, v *contextValue, classes ...any) {
+	for _, c := range classes {
+		switch ccc := c.(type) {
+		case ComponentCSSClass:
+			if !v.hasClassBeenRendered(ccc.ID) {
+				sb.WriteString(string(ccc.Class))
+				v.addClass(ccc.ID)
+			}
+		case KeyValue[ComponentCSSClass, bool]:
+			if !ccc.Value {
+				continue
+			}
+			renderCSSItemsToBuilder(sb, v, ccc.Key)
+		case KeyValue[CSSClass, bool]:
+			if !ccc.Value {
+				continue
+			}
+			renderCSSItemsToBuilder(sb, v, ccc.Key)
+		case CSSClasses:
+			renderCSSItemsToBuilder(sb, v, ccc...)
+		case []CSSClass:
+			for _, item := range ccc {
+				renderCSSItemsToBuilder(sb, v, item)
+			}
+		case func() CSSClass:
+			renderCSSItemsToBuilder(sb, v, ccc())
+		case []string:
+			// Skip. These are class names, not CSS classes.
+		case string:
+			// Skip. This is a class name, not a CSS class.
+		case ConstantCSSClass:
+			// Skip. This is a class name, not a CSS class.
+		case CSSClass:
+			// Skip. This is a class name, not a CSS class.
+		case map[string]bool:
+			// Skip. These are class names, not CSS classes.
+		case KeyValue[string, bool]:
+			// Skip. These are class names, not CSS classes.
+		case []KeyValue[string, bool]:
+			// Skip. These are class names, not CSS classes.
+		case KeyValue[ConstantCSSClass, bool]:
+			// Skip. These are class names, not CSS classes.
+		case []KeyValue[ConstantCSSClass, bool]:
+			// Skip. These are class names, not CSS classes.
+		}
+	}
+}
+
+// SafeCSS is CSS that has been sanitized.
+type SafeCSS string
+
+type SafeCSSProperty string
+
+var safeCSSPropertyType = reflect.TypeOf(SafeCSSProperty(""))
+
+// SanitizeCSS sanitizes CSS properties to ensure that they are safe.
+func SanitizeCSS[T ~string](property string, value T) SafeCSS {
+	if reflect.TypeOf(value) == safeCSSPropertyType {
+		return SafeCSS(safehtml.SanitizeCSSProperty(property) + ":" + string(value) + ";")
+	}
+	p, v := safehtml.SanitizeCSS(property, string(value))
+	return SafeCSS(p + ":" + v + ";")
+}
+
+// Attributes is an alias to map[string]any made for spread attributes.
+type Attributes map[string]any
+
+// sortedKeys returns the keys of a map in sorted order.
+func sortedKeys(m map[string]any) (keys []string) {
+	keys = make([]string, len(m))
+	var i int
+	for k := range m {
+		keys[i] = k
+		i++
+	}
+	sort.Strings(keys)
+	return keys
+}
+
+func writeStrings(w io.Writer, ss ...string) (err error) {
+	for _, s := range ss {
+		if _, err = io.WriteString(w, s); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func RenderAttributes(ctx context.Context, w io.Writer, attributes Attributes) (err error) {
+	for _, key := range sortedKeys(attributes) {
+		value := attributes[key]
+		switch value := value.(type) {
+		case string:
+			if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(value), `"`); err != nil {
+				return err
+			}
+		case *string:
+			if value != nil {
+				if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(*value), `"`); err != nil {
+					return err
+				}
+			}
+		case bool:
+			if value {
+				if err = writeStrings(w, ` `, EscapeString(key)); err != nil {
+					return err
+				}
+			}
+		case *bool:
+			if value != nil && *value {
+				if err = writeStrings(w, ` `, EscapeString(key)); err != nil {
+					return err
+				}
+			}
+		case KeyValue[string, bool]:
+			if value.Value {
+				if err = writeStrings(w, ` `, EscapeString(key), `="`, EscapeString(value.Key), `"`); err != nil {
+					return err
+				}
+			}
+		case KeyValue[bool, bool]:
+			if value.Value && value.Key {
+				if err = writeStrings(w, ` `, EscapeString(key)); err != nil {
+					return err
+				}
+			}
+		case func() bool:
+			if value() {
+				if err = writeStrings(w, ` `, EscapeString(key)); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+// Script handling.
+
+func safeEncodeScriptParams(escapeHTML bool, params []any) []string {
+	encodedParams := make([]string, len(params))
+	for i := 0; i < len(encodedParams); i++ {
+		enc, _ := json.Marshal(params[i])
+		if !escapeHTML {
+			encodedParams[i] = string(enc)
+			continue
+		}
+		encodedParams[i] = EscapeString(string(enc))
+	}
+	return encodedParams
+}
+
+// SafeScript encodes unknown parameters for safety for inside HTML attributes.
+func SafeScript(functionName string, params ...any) string {
+	encodedParams := safeEncodeScriptParams(true, params)
+	sb := new(strings.Builder)
+	sb.WriteString(functionName)
+	sb.WriteRune('(')
+	sb.WriteString(strings.Join(encodedParams, ","))
+	sb.WriteRune(')')
+	return sb.String()
+}
+
+// SafeScript encodes unknown parameters for safety for inline scripts.
+func SafeScriptInline(functionName string, params ...any) string {
+	encodedParams := safeEncodeScriptParams(false, params)
+	sb := new(strings.Builder)
+	sb.WriteString(functionName)
+	sb.WriteRune('(')
+	sb.WriteString(strings.Join(encodedParams, ","))
+	sb.WriteRune(')')
+	return sb.String()
+}
+
+type contextKeyType int
+
+const contextKey = contextKeyType(0)
+
+type contextValue struct {
+	ss          map[string]struct{}
+	onceHandles map[*OnceHandle]struct{}
+	children    *Component
+	nonce       string
+}
+
+func (v *contextValue) setHasBeenRendered(h *OnceHandle) {
+	if v.onceHandles == nil {
+		v.onceHandles = map[*OnceHandle]struct{}{}
+	}
+	v.onceHandles[h] = struct{}{}
+}
+
+func (v *contextValue) getHasBeenRendered(h *OnceHandle) (ok bool) {
+	if v.onceHandles == nil {
+		v.onceHandles = map[*OnceHandle]struct{}{}
+	}
+	_, ok = v.onceHandles[h]
+	return
+}
+
+func (v *contextValue) addScript(s string) {
+	if v.ss == nil {
+		v.ss = map[string]struct{}{}
+	}
+	v.ss["script_"+s] = struct{}{}
+}
+
+func (v *contextValue) hasScriptBeenRendered(s string) (ok bool) {
+	if v.ss == nil {
+		v.ss = map[string]struct{}{}
+	}
+	_, ok = v.ss["script_"+s]
+	return
+}
+
+func (v *contextValue) addClass(s string) {
+	if v.ss == nil {
+		v.ss = map[string]struct{}{}
+	}
+	v.ss["class_"+s] = struct{}{}
+}
+
+func (v *contextValue) hasClassBeenRendered(s string) (ok bool) {
+	if v.ss == nil {
+		v.ss = map[string]struct{}{}
+	}
+	_, ok = v.ss["class_"+s]
+	return
+}
+
+// InitializeContext initializes context used to store internal state used during rendering.
+func InitializeContext(ctx context.Context) context.Context {
+	if _, ok := ctx.Value(contextKey).(*contextValue); ok {
+		return ctx
+	}
+	v := &contextValue{}
+	ctx = context.WithValue(ctx, contextKey, v)
+	return ctx
+}
+
+func getContext(ctx context.Context) (context.Context, *contextValue) {
+	v, ok := ctx.Value(contextKey).(*contextValue)
+	if !ok {
+		ctx = InitializeContext(ctx)
+		v = ctx.Value(contextKey).(*contextValue)
+	}
+	return ctx, v
+}
+
+// ComponentScript is a templ Script template.
+type ComponentScript struct {
+	// Name of the script, e.g. print.
+	Name string
+	// Function to render.
+	Function string
+	// Call of the function in JavaScript syntax, including parameters, and
+	// ensures parameters are HTML escaped; useful for injecting into HTML
+	// attributes like onclick, onhover, etc.
+	//
+	// Given:
+	//    functionName("some string",12345)
+	// It would render:
+	//    __templ_functionName_sha(&#34;some string&#34;,12345))
+	//
+	// This is can be injected into HTML attributes:
+	//    <button onClick="__templ_functionName_sha(&#34;some string&#34;,12345))">Click Me</button>
+	Call string
+	// Call of the function in JavaScript syntax, including parameters. It
+	// does not HTML escape parameters; useful for directly calling in script
+	// elements.
+	//
+	// Given:
+	//    functionName("some string",12345)
+	// It would render:
+	//    __templ_functionName_sha("some string",12345))
+	//
+	// This is can be used to call the function inside a script tag:
+	//    <script>__templ_functionName_sha("some string",12345))</script>
+	CallInline string
+}
+
+var _ Component = ComponentScript{}
+
+func writeScriptHeader(ctx context.Context, w io.Writer) (err error) {
+	var nonceAttr string
+	if nonce := GetNonce(ctx); nonce != "" {
+		nonceAttr = " nonce=\"" + EscapeString(nonce) + "\""
+	}
+	_, err = fmt.Fprintf(w, `<script type="text/javascript"%s>`, nonceAttr)
+	return err
+}
+
+func (c ComponentScript) Render(ctx context.Context, w io.Writer) error {
+	err := RenderScriptItems(ctx, w, c)
+	if err != nil {
+		return err
+	}
+	if len(c.Call) > 0 {
+		if err = writeScriptHeader(ctx, w); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, c.CallInline); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, `</script>`); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// RenderScriptItems renders a <script> element, if the script has not already been rendered.
+func RenderScriptItems(ctx context.Context, w io.Writer, scripts ...ComponentScript) (err error) {
+	if len(scripts) == 0 {
+		return nil
+	}
+	_, v := getContext(ctx)
+	sb := new(strings.Builder)
+	for _, s := range scripts {
+		if !v.hasScriptBeenRendered(s.Name) {
+			sb.WriteString(s.Function)
+			v.addScript(s.Name)
+		}
+	}
+	if sb.Len() > 0 {
+		if err = writeScriptHeader(ctx, w); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, sb.String()); err != nil {
+			return err
+		}
+		if _, err = io.WriteString(w, `</script>`); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+var bufferPool = sync.Pool{
+	New: func() any {
+		return new(bytes.Buffer)
+	},
+}
+
+func GetBuffer() *bytes.Buffer {
+	return bufferPool.Get().(*bytes.Buffer)
+}
+
+func ReleaseBuffer(b *bytes.Buffer) {
+	b.Reset()
+	bufferPool.Put(b)
+}
+
+// JoinStringErrs joins an optional list of errors.
+func JoinStringErrs(s string, errs ...error) (string, error) {
+	return s, errors.Join(errs...)
+}
+
+// Error returned during template rendering.
+type Error struct {
+	Err error
+	// FileName of the template file.
+	FileName string
+	// Line index of the error.
+	Line int
+	// Col index of the error.
+	Col int
+}
+
+func (e Error) Error() string {
+	if e.FileName == "" {
+		e.FileName = "templ"
+	}
+	return fmt.Sprintf("%s: error at line %d, col %d: %v", e.FileName, e.Line, e.Col, e.Err)
+}
+
+func (e Error) Unwrap() error {
+	return e.Err
+}
+
+// Raw renders the input HTML to the output without applying HTML escaping.
+//
+// Use of this component presents a security risk - the HTML should come from
+// a trusted source, because it will be included as-is in the output.
+func Raw[T ~string](html T, errs ...error) Component {
+	return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+		if err = errors.Join(errs...); err != nil {
+			return err
+		}
+		_, err = io.WriteString(w, string(html))
+		return err
+	})
+}
+
+// FromGoHTML creates a templ Component from a Go html/template template.
+func FromGoHTML(t *template.Template, data any) Component {
+	return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+		return t.Execute(w, data)
+	})
+}
+
+// ToGoHTML renders the component to a Go html/template template.HTML string.
+func ToGoHTML(ctx context.Context, c Component) (s template.HTML, err error) {
+	b := GetBuffer()
+	defer ReleaseBuffer(b)
+	if err = c.Render(ctx, b); err != nil {
+		return
+	}
+	s = template.HTML(b.String())
+	return
+}
+
+// WriteWatchModeString is used when rendering templates in development mode.
+// the generator would have written non-go code to the _templ.txt file, which
+// is then read by this function and written to the output.
+func WriteWatchModeString(w io.Writer, lineNum int) error {
+	_, path, _, _ := runtime.Caller(1)
+	if !strings.HasSuffix(path, "_templ.go") {
+		return errors.New("templ: WriteWatchModeString can only be called from _templ.go")
+	}
+	txtFilePath := strings.Replace(path, "_templ.go", "_templ.txt", 1)
+
+	literals, err := getWatchedStrings(txtFilePath)
+	if err != nil {
+		return fmt.Errorf("templ: failed to cache strings: %w", err)
+	}
+
+	if lineNum > len(literals) {
+		return errors.New("templ: failed to find line " + strconv.Itoa(lineNum) + " in " + txtFilePath)
+	}
+
+	unquoted, err := strconv.Unquote(`"` + literals[lineNum-1] + `"`)
+	if err != nil {
+		return err
+	}
+	_, err = io.WriteString(w, unquoted)
+	return err
+}
+
+var (
+	watchModeCache  = map[string]watchState{}
+	watchStateMutex sync.Mutex
+)
+
+type watchState struct {
+	modTime time.Time
+	strings []string
+}
+
+func getWatchedStrings(txtFilePath string) ([]string, error) {
+	watchStateMutex.Lock()
+	defer watchStateMutex.Unlock()
+
+	state, cached := watchModeCache[txtFilePath]
+	if !cached {
+		return cacheStrings(txtFilePath)
+	}
+
+	if time.Since(state.modTime) < time.Millisecond*100 {
+		return state.strings, nil
+	}
+
+	info, err := os.Stat(txtFilePath)
+	if err != nil {
+		return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err)
+	}
+
+	if !info.ModTime().After(state.modTime) {
+		return state.strings, nil
+	}
+
+	return cacheStrings(txtFilePath)
+}
+
+func cacheStrings(txtFilePath string) ([]string, error) {
+	txtFile, err := os.Open(txtFilePath)
+	if err != nil {
+		return nil, fmt.Errorf("templ: failed to open %s: %w", txtFilePath, err)
+	}
+	defer txtFile.Close()
+
+	info, err := txtFile.Stat()
+	if err != nil {
+		return nil, fmt.Errorf("templ: failed to stat %s: %w", txtFilePath, err)
+	}
+
+	all, err := io.ReadAll(txtFile)
+	if err != nil {
+		return nil, fmt.Errorf("templ: failed to read %s: %w", txtFilePath, err)
+	}
+
+	literals := strings.Split(string(all), "\n")
+	watchModeCache[txtFilePath] = watchState{
+		modTime: info.ModTime(),
+		strings: literals,
+	}
+
+	return literals, nil
+}
diff --git a/vendor/github.com/a-h/templ/runtime/buffer.go b/vendor/github.com/a-h/templ/runtime/buffer.go
new file mode 100644
index 0000000..63e4acd
--- /dev/null
+++ b/vendor/github.com/a-h/templ/runtime/buffer.go
@@ -0,0 +1,62 @@
+package runtime
+
+import (
+	"bufio"
+	"io"
+	"net/http"
+)
+
+// DefaultBufferSize is the default size of buffers. It is set to 4KB by default, which is the
+// same as the default buffer size of bufio.Writer.
+var DefaultBufferSize = 4 * 1024 // 4KB
+
+// Buffer is a wrapper around bufio.Writer that enables flushing and closing of
+// the underlying writer.
+type Buffer struct {
+	Underlying io.Writer
+	b          *bufio.Writer
+}
+
+// Write the contents of p into the buffer.
+func (b *Buffer) Write(p []byte) (n int, err error) {
+	return b.b.Write(p)
+}
+
+// Flush writes any buffered data to the underlying io.Writer and
+// calls the Flush method of the underlying http.Flusher if it implements it.
+func (b *Buffer) Flush() error {
+	if err := b.b.Flush(); err != nil {
+		return err
+	}
+	if f, ok := b.Underlying.(http.Flusher); ok {
+		f.Flush()
+	}
+	return nil
+}
+
+// Close closes the buffer and the underlying io.Writer if it implements io.Closer.
+func (b *Buffer) Close() error {
+	if c, ok := b.Underlying.(io.Closer); ok {
+		return c.Close()
+	}
+	return nil
+}
+
+// Reset sets the underlying io.Writer to w and resets the buffer.
+func (b *Buffer) Reset(w io.Writer) {
+	if b.b == nil {
+		b.b = bufio.NewWriterSize(b, DefaultBufferSize)
+	}
+	b.Underlying = w
+	b.b.Reset(w)
+}
+
+// Size returns the size of the underlying buffer in bytes.
+func (b *Buffer) Size() int {
+	return b.b.Size()
+}
+
+// WriteString writes the contents of s into the buffer.
+func (b *Buffer) WriteString(s string) (n int, err error) {
+	return b.b.WriteString(s)
+}
diff --git a/vendor/github.com/a-h/templ/runtime/bufferpool.go b/vendor/github.com/a-h/templ/runtime/bufferpool.go
new file mode 100644
index 0000000..ca2a131
--- /dev/null
+++ b/vendor/github.com/a-h/templ/runtime/bufferpool.go
@@ -0,0 +1,38 @@
+package runtime
+
+import (
+	"io"
+	"sync"
+)
+
+var bufferPool = sync.Pool{
+	New: func() any {
+		return new(Buffer)
+	},
+}
+
+// GetBuffer creates and returns a new buffer if the writer is not already a buffer,
+// or returns the existing buffer if it is.
+func GetBuffer(w io.Writer) (b *Buffer, existing bool) {
+	if w == nil {
+		return nil, false
+	}
+	b, ok := w.(*Buffer)
+	if ok {
+		return b, true
+	}
+	b = bufferPool.Get().(*Buffer)
+	b.Reset(w)
+	return b, false
+}
+
+// ReleaseBuffer flushes the buffer and returns it to the pool.
+func ReleaseBuffer(w io.Writer) (err error) {
+	b, ok := w.(*Buffer)
+	if !ok {
+		return nil
+	}
+	err = b.Flush()
+	bufferPool.Put(b)
+	return err
+}
diff --git a/vendor/github.com/a-h/templ/runtime/builder.go b/vendor/github.com/a-h/templ/runtime/builder.go
new file mode 100644
index 0000000..0f4c9d4
--- /dev/null
+++ b/vendor/github.com/a-h/templ/runtime/builder.go
@@ -0,0 +1,8 @@
+package runtime
+
+import "strings"
+
+// GetBuilder returns a strings.Builder.
+func GetBuilder() (sb strings.Builder) {
+	return sb
+}
diff --git a/vendor/github.com/a-h/templ/runtime/runtime.go b/vendor/github.com/a-h/templ/runtime/runtime.go
new file mode 100644
index 0000000..aaa4a2c
--- /dev/null
+++ b/vendor/github.com/a-h/templ/runtime/runtime.go
@@ -0,0 +1,21 @@
+package runtime
+
+import (
+	"context"
+	"io"
+
+	"github.com/a-h/templ"
+)
+
+// GeneratedComponentInput is used to avoid generated code needing to import the `context` and `io` packages.
+type GeneratedComponentInput struct {
+	Context context.Context
+	Writer  io.Writer
+}
+
+// GeneratedTemplate is used to avoid generated code needing to import the `context` and `io` packages.
+func GeneratedTemplate(f func(GeneratedComponentInput) error) templ.Component {
+	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
+		return f(GeneratedComponentInput{ctx, w})
+	})
+}
diff --git a/vendor/github.com/a-h/templ/safehtml/style.go b/vendor/github.com/a-h/templ/safehtml/style.go
new file mode 100644
index 0000000..486df7c
--- /dev/null
+++ b/vendor/github.com/a-h/templ/safehtml/style.go
@@ -0,0 +1,168 @@
+// Adapted from https://raw.githubusercontent.com/google/safehtml/3c4cd5b5d8c9a6c5882fba099979e9f50b65c876/style.go
+
+// Copyright (c) 2017 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+
+package safehtml
+
+import (
+	"net/url"
+	"regexp"
+	"strings"
+)
+
+// SanitizeCSS attempts to sanitize CSS properties.
+func SanitizeCSS(property, value string) (string, string) {
+	property = SanitizeCSSProperty(property)
+	if property == InnocuousPropertyName {
+		return InnocuousPropertyName, InnocuousPropertyValue
+	}
+	return property, SanitizeCSSValue(property, value)
+}
+
+func SanitizeCSSValue(property, value string) string {
+	if sanitizer, ok := cssPropertyNameToValueSanitizer[property]; ok {
+		return sanitizer(value)
+	}
+	return sanitizeRegular(value)
+}
+
+func SanitizeCSSProperty(property string) string {
+	if !identifierPattern.MatchString(property) {
+		return InnocuousPropertyName
+	}
+	return strings.ToLower(property)
+}
+
+// identifierPattern matches a subset of valid <ident-token> values defined in
+// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram. This pattern matches all generic family name
+// keywords defined in https://drafts.csswg.org/css-fonts-3/#family-name-value.
+var identifierPattern = regexp.MustCompile(`^[-a-zA-Z]+$`)
+
+var cssPropertyNameToValueSanitizer = map[string]func(string) string{
+	"background-image":    sanitizeBackgroundImage,
+	"font-family":         sanitizeFontFamily,
+	"display":             sanitizeEnum,
+	"background-color":    sanitizeRegular,
+	"background-position": sanitizeRegular,
+	"background-repeat":   sanitizeRegular,
+	"background-size":     sanitizeRegular,
+	"color":               sanitizeRegular,
+	"height":              sanitizeRegular,
+	"width":               sanitizeRegular,
+	"left":                sanitizeRegular,
+	"right":               sanitizeRegular,
+	"top":                 sanitizeRegular,
+	"bottom":              sanitizeRegular,
+	"font-weight":         sanitizeRegular,
+	"padding":             sanitizeRegular,
+	"z-index":             sanitizeRegular,
+}
+
+var validURLPrefixes = []string{
+	`url("`,
+	`url('`,
+	`url(`,
+}
+
+var validURLSuffixes = []string{
+	`")`,
+	`')`,
+	`)`,
+}
+
+func sanitizeBackgroundImage(v string) string {
+	// Check for <> as per https://github.com/google/safehtml/blob/be23134998433fcf0135dda53593fc8f8bf4df7c/style.go#L87C2-L89C3
+	if strings.ContainsAny(v, "<>") {
+		return InnocuousPropertyValue
+	}
+	for _, u := range strings.Split(v, ",") {
+		u = strings.TrimSpace(u)
+		var found bool
+		for i, prefix := range validURLPrefixes {
+			if strings.HasPrefix(u, prefix) && strings.HasSuffix(u, validURLSuffixes[i]) {
+				found = true
+				u = strings.TrimPrefix(u, validURLPrefixes[i])
+				u = strings.TrimSuffix(u, validURLSuffixes[i])
+				break
+			}
+		}
+		if !found || !urlIsSafe(u) {
+			return InnocuousPropertyValue
+		}
+	}
+	return v
+}
+
+func urlIsSafe(s string) bool {
+	u, err := url.Parse(s)
+	if err != nil {
+		return false
+	}
+	if u.IsAbs() {
+		if strings.EqualFold(u.Scheme, "http") || strings.EqualFold(u.Scheme, "https") || strings.EqualFold(u.Scheme, "mailto") {
+			return true
+		}
+		return false
+	}
+	return true
+}
+
+var genericFontFamilyName = regexp.MustCompile(`^[a-zA-Z][- a-zA-Z]+$`)
+
+func sanitizeFontFamily(s string) string {
+	for _, f := range strings.Split(s, ",") {
+		f = strings.TrimSpace(f)
+		if strings.HasPrefix(f, `"`) {
+			if !strings.HasSuffix(f, `"`) {
+				return InnocuousPropertyValue
+			}
+			continue
+		}
+		if !genericFontFamilyName.MatchString(f) {
+			return InnocuousPropertyValue
+		}
+	}
+	return s
+}
+
+func sanitizeEnum(s string) string {
+	if !safeEnumPropertyValuePattern.MatchString(s) {
+		return InnocuousPropertyValue
+	}
+	return s
+}
+
+func sanitizeRegular(s string) string {
+	if !safeRegularPropertyValuePattern.MatchString(s) {
+		return InnocuousPropertyValue
+	}
+	return s
+}
+
+// InnocuousPropertyName is an innocuous property generated by a sanitizer when its input is unsafe.
+const InnocuousPropertyName = "zTemplUnsafeCSSPropertyName"
+
+// InnocuousPropertyValue is an innocuous property generated by a sanitizer when its input is unsafe.
+const InnocuousPropertyValue = "zTemplUnsafeCSSPropertyValue"
+
+// safeRegularPropertyValuePattern matches strings that are safe to use as property values.
+// Specifically, it matches string where every '*' or '/' is followed by end-of-text or a safe rune
+// (i.e. alphanumerics or runes in the set [+-.!#%_ \t]). This regex ensures that the following
+// are disallowed:
+//   - "/*" and "*/", which are CSS comment markers.
+//   - "//", even though this is not a comment marker in the CSS specification. Disallowing
+//     this string minimizes the chance that browser peculiarities or parsing bugs will allow
+//     sanitization to be bypassed.
+//   - '(' and ')', which can be used to call functions.
+//   - ',', since it can be used to inject extra values into a property.
+//   - Runes which could be matched on CSS error recovery of a previously malformed token, such as '@'
+//     and ':'. See http://www.w3.org/TR/css3-syntax/#error-handling.
+var safeRegularPropertyValuePattern = regexp.MustCompile(`^(?:[*/]?(?:[0-9a-zA-Z+-.!#%_ \t]|$))*$`)
+
+// safeEnumPropertyValuePattern matches strings that are safe to use as enumerated property values.
+// Specifically, it matches strings that contain only alphabetic and '-' runes.
+var safeEnumPropertyValuePattern = regexp.MustCompile(`^[a-zA-Z-]*$`)
diff --git a/vendor/github.com/a-h/templ/templ.png b/vendor/github.com/a-h/templ/templ.png
new file mode 100644
index 0000000..1c4bc2f
Binary files /dev/null and b/vendor/github.com/a-h/templ/templ.png differ
diff --git a/vendor/github.com/a-h/templ/url.go b/vendor/github.com/a-h/templ/url.go
new file mode 100644
index 0000000..bf912e1
--- /dev/null
+++ b/vendor/github.com/a-h/templ/url.go
@@ -0,0 +1,20 @@
+package templ
+
+import "strings"
+
+// FailedSanitizationURL is returned if a URL fails sanitization checks.
+const FailedSanitizationURL = SafeURL("about:invalid#TemplFailedSanitizationURL")
+
+// URL sanitizes the input string s and returns a SafeURL.
+func URL(s string) SafeURL {
+	if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
+		protocol := s[:i]
+		if !strings.EqualFold(protocol, "http") && !strings.EqualFold(protocol, "https") && !strings.EqualFold(protocol, "mailto") && !strings.EqualFold(protocol, "tel") && !strings.EqualFold(protocol, "ftp") && !strings.EqualFold(protocol, "ftps") {
+			return FailedSanitizationURL
+		}
+	}
+	return SafeURL(s)
+}
+
+// SafeURL is a URL that has been sanitized.
+type SafeURL string
diff --git a/vendor/github.com/a-h/templ/version.go b/vendor/github.com/a-h/templ/version.go
new file mode 100644
index 0000000..b7fbb6f
--- /dev/null
+++ b/vendor/github.com/a-h/templ/version.go
@@ -0,0 +1,10 @@
+package templ
+
+import _ "embed"
+
+//go:embed .version
+var version string
+
+func Version() string {
+	return "v" + version
+}
-- 
cgit v1.2.3