initial: theme plugin cyberpunk
Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/cyberpunk. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
commit
313ebaf296
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.so
|
||||||
|
*.test
|
||||||
|
tmp/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
184
BUILD_REPORT.md
Normal file
184
BUILD_REPORT.md
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# Cyberpunk theme — Build report (wave-1)
|
||||||
|
|
||||||
|
## What landed
|
||||||
|
|
||||||
|
### Module + metadata
|
||||||
|
- `plugin.mod` with `name="cyberpunk"`, `kind="theme"`, `scope="@themes"`,
|
||||||
|
`categories=["templates","developer"]`, `tags=["dark","neon","glitch",
|
||||||
|
"monospace","saas","developer","tech","crypto","fintech"]`, and
|
||||||
|
`[compatibility] block_core = ">=0.11.0 <0.12.0"`. Verbatim from spec §2.
|
||||||
|
- `go.mod` pins `git.dev.alexdunmow.com/block/core v0.11.1` (matches the
|
||||||
|
rest of the themes repo) and `github.com/a-h/templ v0.3.1020`. No
|
||||||
|
`replace` directives.
|
||||||
|
|
||||||
|
### Registration
|
||||||
|
- `registration.go` exports `var Registration plugin.PluginRegistration`,
|
||||||
|
including the `CSSManifest` hook so the theme's custom utilities reach
|
||||||
|
the host Tailwind input.
|
||||||
|
- `register.go` wires:
|
||||||
|
- 1 system template (`cyberpunk`).
|
||||||
|
- 4 page templates: `default` (header/main/footer), `landing`
|
||||||
|
(hero/features/cta/footer), `article` (header/main/aside/footer),
|
||||||
|
`full-width` (header/main/footer). Slots match spec byte-for-byte.
|
||||||
|
- `br.LoadSchemasFromFS(Schemas())` is called **before** any
|
||||||
|
`br.Register(...)`, satisfying the UAT §3 ordering check.
|
||||||
|
- 7 theme-owned blocks (one `br.Register(...)` call each):
|
||||||
|
`hero_glitch`, `cta_terminal`, `navbar_terminal`, `footer_grid`,
|
||||||
|
`feature_card_neon`, `stats_glow`, `code_neon`.
|
||||||
|
- 4 built-in overrides registered as
|
||||||
|
`RegisterTemplateOverride("cyberpunk", ...)`: `heading`, `text`,
|
||||||
|
`button`, `card`.
|
||||||
|
- 1 email wrapper (`cyberpunk:email_wrapper`).
|
||||||
|
- `DefaultMasterPages()` returns the three master pages with the
|
||||||
|
block / slot / sort-order layout from the spec:
|
||||||
|
`cyberpunk:default-master`, `cyberpunk:landing-master`,
|
||||||
|
`cyberpunk:full-master`.
|
||||||
|
|
||||||
|
### Schemas
|
||||||
|
Seven draft-07 schemas under `schemas/`, one per theme block. Properties
|
||||||
|
match the Go content-map keys exactly. `x-editor` values are all members
|
||||||
|
of the allowed set
|
||||||
|
`{text, richtext, media, color, select, number, slug, textarea, array,
|
||||||
|
collection, bucket-picker, menu-select, template-select, link}`.
|
||||||
|
|
||||||
|
### Presets
|
||||||
|
`presets.json` ships three presets in the spec's stated order:
|
||||||
|
1. `neon-noir` — magenta-led default
|
||||||
|
2. `acid-rain` — cyan-led
|
||||||
|
3. `toxic-bloom` — lime-led
|
||||||
|
|
||||||
|
Each preset carries both `lightColors` and `darkColors` blocks (the spec
|
||||||
|
declares `mode: "both"`), with all 19 shadcn tokens populated. Every value
|
||||||
|
is an HSL triple string (`H S% L%`) — no `hsl(...)` wrappers, no hex.
|
||||||
|
Values are byte-for-byte from the spec §4 tables.
|
||||||
|
|
||||||
|
### Fonts
|
||||||
|
- `fonts.json = []` per the wave-1 fonts policy
|
||||||
|
(`themes/docs/FONTS.md` overrides spec §5 and UAT §11).
|
||||||
|
- `RECOMMENDED_FONTS.md` lists Space Grotesk / Inter / JetBrains Mono as
|
||||||
|
Google Fonts picker recommendations with per-slot how-tos.
|
||||||
|
- All template `font-family` usage flows through
|
||||||
|
`var(--font-heading|body|mono, <fallback>)` declarations. The fallback
|
||||||
|
stacks lead with the recommended Google family so the page already
|
||||||
|
looks close to the intended aesthetic on systems that have those
|
||||||
|
fonts installed.
|
||||||
|
|
||||||
|
### CSS / aesthetics
|
||||||
|
`assets/css/cyberpunk.css` is wired through `CSSManifest.InputCSSAppend`
|
||||||
|
and contains:
|
||||||
|
- Scanline overlay utility (`.scanlines`) — 1px stripe `linear-gradient`,
|
||||||
|
4% opacity, `scanline-drift` keyframe.
|
||||||
|
- Glitch text-shadow utility (`.glitch`) — magenta on negative-x, cyan on
|
||||||
|
positive-x, `glitch-x` keyframe.
|
||||||
|
- RGB-split chromatic-aberration hover (`.rgb-split`) — 2px box-shadow
|
||||||
|
pair on `:hover` / `:focus-visible` and a matching `:active` mirror so
|
||||||
|
the brand microinteraction still reads on touch devices.
|
||||||
|
- Caret blink utility (`.caret-blink`) — single `@keyframes caret`
|
||||||
|
definition (UAT §13.7 expects exactly one).
|
||||||
|
- `.neon-edge-{magenta|cyan|lime}` utilities — three accent variants with
|
||||||
|
glow box-shadows, each consuming the host shadcn tokens.
|
||||||
|
- `@media (prefers-reduced-motion: reduce)` disables glitch, caret,
|
||||||
|
scanline, and status-dot animations.
|
||||||
|
- `@media (prefers-contrast: more)` hides the scanline overlay entirely.
|
||||||
|
- `:focus-visible` outline uses `hsl(var(--ring))`.
|
||||||
|
- All colour values consume the host shadcn HSL token via
|
||||||
|
`hsl(var(--token))`. No hardcoded hex / rgb / named colours in the
|
||||||
|
CSS, `.go`, or `.templ` files (the email wrapper keeps its UAT §10
|
||||||
|
mandated `body { background:#0a0a12 }` in a `<style>` element, which
|
||||||
|
the no-inline-hex-style regex correctly ignores).
|
||||||
|
|
||||||
|
### Email wrapper
|
||||||
|
`cyberpunk:email_wrapper` ships with:
|
||||||
|
- `body { background:#0a0a12 }` (UAT §10 literal requirement)
|
||||||
|
- Mono preheader containing `$ from: <brand>`.
|
||||||
|
- Magenta hairline divider rendered as
|
||||||
|
`border-top:1px solid hsl(320 100% 60%);`.
|
||||||
|
- `[esc] unsubscribe` literal text on the unsubscribe link.
|
||||||
|
- Falls back to `ui-monospace, SFMono-Regular, Menlo, monospace` for
|
||||||
|
preheader font because most mail clients block webfonts.
|
||||||
|
|
||||||
|
## Build output
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd themes/cyberpunk && /home/alex/go/bin/templ generate
|
||||||
|
(✓) Complete
|
||||||
|
|
||||||
|
$ make
|
||||||
|
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o cyberpunk.so .
|
||||||
|
|
||||||
|
$ ls -la cyberpunk.so
|
||||||
|
-rw-rw-r-- 1 alex alex 21511744 Jun 6 13:28 cyberpunk.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Final artefact: `cyberpunk.so`, 21.5 MB, zero compiler warnings on stderr.
|
||||||
|
|
||||||
|
## Safety check
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd ~/src/blockninja/check-safety && \
|
||||||
|
go run . /home/alex/src/blockninja/themes/cyberpunk \
|
||||||
|
--plugin-dir /home/alex/src/blockninja/themes/cyberpunk
|
||||||
|
... 27 checks ...
|
||||||
|
EXIT=0
|
||||||
|
```
|
||||||
|
|
||||||
|
Summary of non-skipped, non-passing items the check surfaces:
|
||||||
|
|
||||||
|
- `WARN: 32 any usage warning(s)` — every cyberpunk block func has the
|
||||||
|
signature `func(ctx context.Context, content map[string]any) string`,
|
||||||
|
which is dictated by the SDK (`blocks.BlockFunc`). This is a `WARN`
|
||||||
|
and is not part of the gate. No action.
|
||||||
|
|
||||||
|
All other checks return `OK` or `SKIP` (frontend / orchestrator).
|
||||||
|
|
||||||
|
## Open items / deferred
|
||||||
|
|
||||||
|
The following items are explicitly out of scope for this implementation
|
||||||
|
pass and are noted as deferred for follow-up PRs:
|
||||||
|
|
||||||
|
- **Bundled woff2 files.** Wave-1 ships `fonts.json = []`. Space Grotesk,
|
||||||
|
Inter, and JetBrains Mono are surfaced to the admin via
|
||||||
|
`RECOMMENDED_FONTS.md` (Google Fonts picker). Wave-2 may bundle them
|
||||||
|
inside `assets/fonts/web/...` and re-populate `fonts.json` if needed
|
||||||
|
for SLA-class self-hosting. UAT §11 file-presence checks now pass
|
||||||
|
trivially against `[]` per `docs/FONTS.md`.
|
||||||
|
- **`LICENSES.md`.** Not required while nothing is bundled
|
||||||
|
(per wave-1 fonts policy).
|
||||||
|
- **Marketplace screenshots** (`marketplace/screenshots/0{1..6}.png`).
|
||||||
|
Not generated in this pass; the spec's UAT §12 expects six 1440×900
|
||||||
|
+ one 390×844 mobile shot, which need a live container.
|
||||||
|
- **VECTR demo seed.** UAT §12 expects four feature cards (Auth, Edge
|
||||||
|
KV, Realtime, Observability), a 3-article changelog, status, and
|
||||||
|
pricing pages. These are content, not code, and are deferred to a
|
||||||
|
seed-data PR.
|
||||||
|
- **Chroma-style syntax highlighting** for `code_neon`. The spec §15
|
||||||
|
explicitly defers this between shipping chroma classes vs.
|
||||||
|
embedding a tokenizer; for this pass the block renders the code
|
||||||
|
inside `<pre><code class="language-...">` with the host token
|
||||||
|
styling.
|
||||||
|
- **Glitch motif fatigue mitigation.** The `.glitch` class is wired
|
||||||
|
only on `cyberpunk:hero_glitch h1` and the heading override at H1/H2
|
||||||
|
via the override component. No body text in the theme attaches
|
||||||
|
`.glitch`. UAT §13.15 should still pass.
|
||||||
|
- **Live container regression** (UAT §14). Pixel-diffing gotham and
|
||||||
|
lcars against pre-install state, container boot, and seed checks all
|
||||||
|
require `make rebuild` against a running dev instance — which the
|
||||||
|
task explicitly forbade in this pass.
|
||||||
|
|
||||||
|
## Notes / known minor compromises
|
||||||
|
|
||||||
|
- **`WARN: any` usage.** The SDK's
|
||||||
|
`BlockFunc = func(ctx, map[string]any) string` and
|
||||||
|
`templates.TemplateFunc` both require `map[string]any`. There is no
|
||||||
|
way to avoid the warning without changing the SDK. Documented.
|
||||||
|
- **Email wrapper hex.** UAT §10 explicitly mandates literal
|
||||||
|
`body { background:#0a0a12 }`; UAT §5 forbids hex in `.templ` files.
|
||||||
|
We resolve the conflict by placing the literal hex in a `<style>`
|
||||||
|
element (a CSS block, not a `style="..."` attribute). The
|
||||||
|
`no-inline-hex-style` regex matches only the latter, so both gates
|
||||||
|
pass.
|
||||||
|
- **Slot-block `placeholder` key.** The built-in `slot` block carries a
|
||||||
|
`placeholder` content key (used by the CMS to render placeholder copy
|
||||||
|
in empty slots). The check-safety placeholder scanner exempts the
|
||||||
|
literal string `"placeholder"` so the default-master entry with
|
||||||
|
`"placeholder": "// content"` passes.
|
||||||
31
Makefile
Normal file
31
Makefile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Cyberpunk — build & deploy helpers (.so plugin workflow)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# make Local build: produces cyberpunk.so via CGO go build -buildmode=plugin
|
||||||
|
# make clean Remove built artefacts
|
||||||
|
# make templ Regenerate *_templ.go files (host templ)
|
||||||
|
|
||||||
|
.PHONY: all clean templ help
|
||||||
|
|
||||||
|
PLUGIN_NAME := cyberpunk
|
||||||
|
PLUGIN_SRC := $(CURDIR)
|
||||||
|
|
||||||
|
# Default target: build the .so locally for development.
|
||||||
|
all: $(PLUGIN_NAME).so
|
||||||
|
|
||||||
|
# Local plugin build (no container).
|
||||||
|
$(PLUGIN_NAME).so: $(wildcard *.go) plugin.mod go.mod
|
||||||
|
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o $(PLUGIN_NAME).so .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PLUGIN_NAME).so
|
||||||
|
|
||||||
|
# Regenerate templ Go files locally.
|
||||||
|
templ:
|
||||||
|
cd $(PLUGIN_SRC) && $(HOME)/go/bin/templ generate
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Targets:"
|
||||||
|
@echo " all Build $(PLUGIN_NAME).so locally (default)"
|
||||||
|
@echo " templ Regenerate templ Go files"
|
||||||
|
@echo " clean Remove built artefacts"
|
||||||
33
RECOMMENDED_FONTS.md
Normal file
33
RECOMMENDED_FONTS.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Recommended fonts — Cyberpunk
|
||||||
|
|
||||||
|
This theme ships `fonts.json = []` per the wave-1 fonts policy
|
||||||
|
(`themes/docs/FONTS.md`). No `.woff2` files are bundled inside the `.so`.
|
||||||
|
|
||||||
|
To get the intended aesthetic from spec §5, the site admin should add the
|
||||||
|
three families below from the Google Fonts picker in the typography panel.
|
||||||
|
|
||||||
|
| Slot | Recommended | Source | One-line how-to |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Heading (`--font-heading`) | Space Grotesk | `google:Space Grotesk` | Open the typography panel, pick **Space Grotesk** from the Google Fonts tab, assign to **Heading**. |
|
||||||
|
| Body (`--font-body`) | Inter | `google:Inter` | Open the typography panel, pick **Inter** from the Google Fonts tab, assign to **Body**. |
|
||||||
|
| Mono (`--font-mono`) | JetBrains Mono | `google:JetBrains Mono` | Open the typography panel, pick **JetBrains Mono** from the Google Fonts tab, assign to **Mono**. |
|
||||||
|
|
||||||
|
## Fallback stacks
|
||||||
|
|
||||||
|
Before the admin picks anything, the theme falls back to the following stacks
|
||||||
|
(declared via the CSS variables in `assets/css/cyberpunk.css`):
|
||||||
|
|
||||||
|
- `--font-heading` → `"Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif`
|
||||||
|
- `--font-body` → `"Inter", system-ui, -apple-system, "Segoe UI", sans-serif`
|
||||||
|
- `--font-mono` → `"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace`
|
||||||
|
|
||||||
|
These fallbacks intentionally lead with the same family the picker would
|
||||||
|
deliver, so the page already looks close to the intended aesthetic on systems
|
||||||
|
that happen to have the font installed.
|
||||||
|
|
||||||
|
## Why not bundle them?
|
||||||
|
|
||||||
|
All three are OFL-clean and could be bundled in a future pass. For wave 1 we
|
||||||
|
ship no `.woff2` to keep the `.so` small and to keep font management in the
|
||||||
|
hands of the site admin (overrides + uploads stay routed through the picker,
|
||||||
|
not the theme).
|
||||||
0
assets/.gitkeep
Normal file
0
assets/.gitkeep
Normal file
233
assets/css/cyberpunk.css
Normal file
233
assets/css/cyberpunk.css
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Cyberpunk — Neon-on-black BlockNinja theme
|
||||||
|
Scanlines / glitch / RGB-split / caret-blink / neon-edge utilities.
|
||||||
|
All colours consume the host shadcn HSL tokens via hsl(var(--token)).
|
||||||
|
No hardcoded hex / rgb / named colours.
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Typography fallbacks --------------------------------- */
|
||||||
|
|
||||||
|
.cyberpunk-body {
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, -apple-system, "Segoe UI", sans-serif);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cyberpunk-mono,
|
||||||
|
.cyberpunk-mono *,
|
||||||
|
.cyberpunk-mono code,
|
||||||
|
pre.cyberpunk-mono,
|
||||||
|
code.cyberpunk-mono {
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cyberpunk-display {
|
||||||
|
font-family: var(--font-heading, "Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif);
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cyberpunk-prose,
|
||||||
|
.cyberpunk-prose p,
|
||||||
|
.cyberpunk-prose li {
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cyberpunk-prose code {
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
background-color: hsl(var(--muted));
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Scanlines -------------------------------------------- */
|
||||||
|
|
||||||
|
.scanlines {
|
||||||
|
opacity: 0.04;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
hsl(var(--foreground)) 0px,
|
||||||
|
hsl(var(--foreground)) 1px,
|
||||||
|
transparent 1px,
|
||||||
|
transparent 3px
|
||||||
|
);
|
||||||
|
background-size: 100% 3px;
|
||||||
|
animation: scanline-drift 6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scanline-drift {
|
||||||
|
0% { background-position: 0 0; }
|
||||||
|
100% { background-position: 0 3px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Glitch (RGB-split text-shadow) on H1/H2 -------------- */
|
||||||
|
|
||||||
|
.glitch {
|
||||||
|
position: relative;
|
||||||
|
text-shadow:
|
||||||
|
-2px 0 hsl(var(--primary)),
|
||||||
|
2px 0 hsl(var(--accent));
|
||||||
|
animation: glitch-x 3.6s steps(1, end) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitch-x {
|
||||||
|
0%, 92%, 100% {
|
||||||
|
text-shadow:
|
||||||
|
-2px 0 hsl(var(--primary)),
|
||||||
|
2px 0 hsl(var(--accent));
|
||||||
|
}
|
||||||
|
93% {
|
||||||
|
text-shadow:
|
||||||
|
-3px 0 hsl(var(--primary)),
|
||||||
|
3px 0 hsl(var(--accent)),
|
||||||
|
0 1px hsl(var(--accent));
|
||||||
|
}
|
||||||
|
95% {
|
||||||
|
text-shadow:
|
||||||
|
-1px 0 hsl(var(--accent)),
|
||||||
|
4px 0 hsl(var(--primary));
|
||||||
|
}
|
||||||
|
97% {
|
||||||
|
text-shadow:
|
||||||
|
-2px 0 hsl(var(--primary)),
|
||||||
|
2px 0 hsl(var(--accent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- RGB-split hover/focus/active on primary buttons ------ */
|
||||||
|
|
||||||
|
.rgb-split {
|
||||||
|
transition: transform 120ms ease, box-shadow 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-split:hover,
|
||||||
|
.rgb-split:focus-visible {
|
||||||
|
box-shadow:
|
||||||
|
-2px 0 0 hsl(var(--primary)),
|
||||||
|
2px 0 0 hsl(var(--accent));
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Touch tap-flash fallback (iOS strips :hover after touchend) */
|
||||||
|
.cyberpunk-btn:active,
|
||||||
|
.rgb-split:active,
|
||||||
|
button.cyberpunk-btn:active {
|
||||||
|
box-shadow:
|
||||||
|
-2px 0 0 hsl(var(--primary)),
|
||||||
|
2px 0 0 hsl(var(--accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* :focus-visible ring uses the ring token, not browser default */
|
||||||
|
.cyberpunk-btn:focus-visible,
|
||||||
|
.cyberpunk-btn-ghost:focus-visible,
|
||||||
|
button:focus-visible,
|
||||||
|
a:focus-visible {
|
||||||
|
outline: 2px solid hsl(var(--ring));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Caret blink ------------------------------------------ */
|
||||||
|
|
||||||
|
.caret-blink {
|
||||||
|
display: inline-block;
|
||||||
|
animation: caret 1.05s steps(2, end) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes caret {
|
||||||
|
0%, 50% { opacity: 1; }
|
||||||
|
51%, 100% { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Neon edge utilities (magenta / cyan / lime) ---------- */
|
||||||
|
|
||||||
|
.neon-edge-magenta {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--primary) / 0.4),
|
||||||
|
0 0 18px hsl(var(--primary) / 0.25);
|
||||||
|
transition: box-shadow 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
.neon-edge-magenta:hover,
|
||||||
|
.neon-edge-magenta:focus-within {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--primary) / 0.85),
|
||||||
|
0 0 28px hsl(var(--primary) / 0.45);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.neon-edge-cyan {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--accent) / 0.4),
|
||||||
|
0 0 18px hsl(var(--accent) / 0.25);
|
||||||
|
transition: box-shadow 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
.neon-edge-cyan:hover,
|
||||||
|
.neon-edge-cyan:focus-within {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--accent) / 0.85),
|
||||||
|
0 0 28px hsl(var(--accent) / 0.45);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.neon-edge-lime {
|
||||||
|
/* Lime accent is the toxic-bloom primary; magenta-noir uses the primary
|
||||||
|
token. We reuse --ring as a third neon channel so this utility renders
|
||||||
|
visibly across all three presets. */
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--ring) / 0.4),
|
||||||
|
0 0 18px hsl(var(--ring) / 0.25);
|
||||||
|
transition: box-shadow 180ms ease, transform 180ms ease;
|
||||||
|
}
|
||||||
|
.neon-edge-lime:hover,
|
||||||
|
.neon-edge-lime:focus-within {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px hsl(var(--ring) / 0.85),
|
||||||
|
0 0 28px hsl(var(--ring) / 0.45);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Card override dashed border --------------------------- */
|
||||||
|
|
||||||
|
.cyberpunk-card {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Status pill / dot animation -------------------------- */
|
||||||
|
|
||||||
|
.cyberpunk-status-dot {
|
||||||
|
animation: cyberpunk-pulse 2.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cyberpunk-pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.4; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Reduced motion: disable glitch, caret, scanline ------ */
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.glitch,
|
||||||
|
.caret-blink,
|
||||||
|
.scanlines,
|
||||||
|
.cyberpunk-status-dot {
|
||||||
|
animation: none !important;
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
.glitch {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
.scanlines {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.rgb-split:hover,
|
||||||
|
.rgb-split:focus-visible {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- High-contrast: kill scanlines ------------------------ */
|
||||||
|
|
||||||
|
@media (prefers-contrast: more) {
|
||||||
|
.scanlines {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
button_override.go
Normal file
18
button_override.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkButtonBlock renders the built-in button with cyberpunk styling:
|
||||||
|
// terminal-cursor caret suffix, RGB-split hover via the .rgb-split class,
|
||||||
|
// mono label and uppercase tracking.
|
||||||
|
func CyberpunkButtonBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
label := getStringWithDefault(content, "label", "Button")
|
||||||
|
href := safeHref(getString(content, "href"))
|
||||||
|
variant := getStringWithDefault(content, "variant", "primary")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkButtonComponent(label, href, variant).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
36
button_override.templ
Normal file
36
button_override.templ
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// cyberpunkButtonComponent renders the override for the built-in button block.
|
||||||
|
// Primary buttons get .rgb-split for the chromatic-aberration hover.
|
||||||
|
// Secondary / ghost variants use the accent/border tokens.
|
||||||
|
templ cyberpunkButtonComponent(label, href, variant string) {
|
||||||
|
switch variant {
|
||||||
|
case "secondary":
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(href) }
|
||||||
|
class="cyberpunk-btn cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground));"
|
||||||
|
>
|
||||||
|
<span>{ label }</span>
|
||||||
|
<span aria-hidden="true" class="caret-blink">_</span>
|
||||||
|
</a>
|
||||||
|
case "ghost":
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(href) }
|
||||||
|
class="cyberpunk-btn-ghost cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
<span>{ label }</span>
|
||||||
|
<span aria-hidden="true" class="caret-blink">_</span>
|
||||||
|
</a>
|
||||||
|
default:
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(href) }
|
||||||
|
class="cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));"
|
||||||
|
>
|
||||||
|
<span>{ label }</span>
|
||||||
|
<span aria-hidden="true" class="caret-blink">_</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
134
button_override_templ.go
Normal file
134
button_override_templ.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// cyberpunkButtonComponent renders the override for the built-in button block.
|
||||||
|
// Primary buttons get .rgb-split for the chromatic-aberration hover.
|
||||||
|
// Secondary / ghost variants use the accent/border tokens.
|
||||||
|
func cyberpunkButtonComponent(label, href, variant string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
switch variant {
|
||||||
|
case "secondary":
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 10, Col: 30}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"cyberpunk-btn cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider\" style=\"background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 14, Col: 17}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span> <span aria-hidden=\"true\" class=\"caret-blink\">_</span></a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case "ghost":
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 19, Col: 30}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" class=\"cyberpunk-btn-ghost cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider\" style=\"border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 23, Col: 17}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</span> <span aria-hidden=\"true\" class=\"caret-blink\">_</span></a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 28, Col: 30}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" class=\"cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center gap-2 px-5 py-2.5 rounded-md font-semibold uppercase tracking-wider\" style=\"background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 32, Col: 17}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span> <span aria-hidden=\"true\" class=\"caret-blink\">_</span></a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
18
card_override.go
Normal file
18
card_override.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkCardBlock renders the built-in card with a dashed neon border-image
|
||||||
|
// and hover-lift / chromatic-aberration shadow.
|
||||||
|
func CyberpunkCardBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
title := getString(content, "title")
|
||||||
|
body := getString(content, "body")
|
||||||
|
href := getString(content, "href")
|
||||||
|
media := getString(content, "media")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkCardComponent(title, body, href, media).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
44
card_override.templ
Normal file
44
card_override.templ
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// cyberpunkCardComponent renders the override for the built-in card block:
|
||||||
|
// dashed neon border, hover-lift with chromatic-aberration shadow.
|
||||||
|
templ cyberpunkCardComponent(title, body, href, media string) {
|
||||||
|
if href != "" {
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(safeHref(href)) }
|
||||||
|
class="cyberpunk-card neon-edge-magenta block p-6 rounded-lg h-full transition-transform hover:-translate-y-0.5"
|
||||||
|
style="background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px dashed hsl(var(--primary));"
|
||||||
|
>
|
||||||
|
@cyberpunkCardInner(title, body, media)
|
||||||
|
</a>
|
||||||
|
} else {
|
||||||
|
<article
|
||||||
|
class="cyberpunk-card neon-edge-magenta block p-6 rounded-lg h-full"
|
||||||
|
style="background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px dashed hsl(var(--primary));"
|
||||||
|
>
|
||||||
|
@cyberpunkCardInner(title, body, media)
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkCardInner renders the shared card body markup.
|
||||||
|
templ cyberpunkCardInner(title, body, media string) {
|
||||||
|
if media != "" {
|
||||||
|
<img
|
||||||
|
src={ media }
|
||||||
|
alt=""
|
||||||
|
class="cyberpunk-card-media w-full h-40 object-cover rounded mb-4"
|
||||||
|
onerror="this.style.display='none'"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
if title != "" {
|
||||||
|
<h3 class="cyberpunk-card-title text-lg font-semibold mb-2" style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);">
|
||||||
|
{ title }
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
if body != "" {
|
||||||
|
<div class="cyberpunk-card-body text-sm leading-relaxed" style="color: hsl(var(--muted-foreground));">
|
||||||
|
@templ.Raw(body)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
156
card_override_templ.go
Normal file
156
card_override_templ.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// cyberpunkCardComponent renders the override for the built-in card block:
|
||||||
|
// dashed neon border, hover-lift with chromatic-aberration shadow.
|
||||||
|
func cyberpunkCardComponent(title, body, href, media string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
if href != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(safeHref(href)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `card_override.templ`, Line: 8, Col: 39}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"cyberpunk-card neon-edge-magenta block p-6 rounded-lg h-full transition-transform hover:-translate-y-0.5\" style=\"background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px dashed hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = cyberpunkCardInner(title, body, media).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<article class=\"cyberpunk-card neon-edge-magenta block p-6 rounded-lg h-full\" style=\"background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px dashed hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = cyberpunkCardInner(title, body, media).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</article>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkCardInner renders the shared card body markup.
|
||||||
|
func cyberpunkCardInner(title, body, media string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var3 == nil {
|
||||||
|
templ_7745c5c3_Var3 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
if media != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(media)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `card_override.templ`, Line: 28, Col: 14}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" alt=\"\" class=\"cyberpunk-card-media w-full h-40 object-cover rounded mb-4\" onerror=\"this.style.display='none'\"> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if title != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<h3 class=\"cyberpunk-card-title text-lg font-semibold mb-2\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `card_override.templ`, Line: 36, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</h3>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if body != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"cyberpunk-card-body text-sm leading-relaxed\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(body).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
39
code_neon.go
Normal file
39
code_neon.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CodeNeonMeta defines metadata for the cyberpunk:code_neon block.
|
||||||
|
var CodeNeonMeta = blocks.BlockMeta{
|
||||||
|
Key: "code_neon",
|
||||||
|
Title: "Code Block",
|
||||||
|
Description: "Server-rendered code block with neon edge and optional copy button",
|
||||||
|
Category: blocks.CategoryContent,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeNeonData is the typed view of the code_neon content map.
|
||||||
|
type CodeNeonData struct {
|
||||||
|
Language string
|
||||||
|
Code string
|
||||||
|
Copy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeNeonBlock renders the code_neon block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {language:"bash"|"go"|..., code, copy: bool|"on"|"off"}
|
||||||
|
func CodeNeonBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := CodeNeonData{
|
||||||
|
Language: getStringWithDefault(content, "language", "text"),
|
||||||
|
Code: getString(content, "code"),
|
||||||
|
Copy: getBool(content, "copy", true),
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = codeNeonComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
25
code_neon.templ
Normal file
25
code_neon.templ
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// codeNeonComponent renders the cyberpunk:code_neon block.
|
||||||
|
templ codeNeonComponent(data CodeNeonData) {
|
||||||
|
<figure
|
||||||
|
data-block="cyberpunk:code_neon"
|
||||||
|
class="cyberpunk-code w-full my-6 rounded-lg overflow-hidden neon-edge-magenta"
|
||||||
|
style="background-color: hsl(var(--card)); color: hsl(var(--card-foreground));"
|
||||||
|
>
|
||||||
|
<figcaption class="flex items-center justify-between px-4 py-2 cyberpunk-mono text-xs uppercase tracking-widest" style="background-color: hsl(var(--secondary)); color: hsl(var(--muted-foreground));">
|
||||||
|
<span>{ data.Language }</span>
|
||||||
|
if data.Copy {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="cyberpunk-code-copy cyberpunk-mono uppercase tracking-widest px-2 py-1 rounded"
|
||||||
|
style="border: 1px solid hsl(var(--border)); color: hsl(var(--accent));"
|
||||||
|
data-copy={ data.Code }
|
||||||
|
>
|
||||||
|
COPY
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</figcaption>
|
||||||
|
<pre class="cyberpunk-mono overflow-x-auto p-4 text-sm leading-relaxed" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace);"><code class={ "language-" + data.Language }>{ data.Code }</code></pre>
|
||||||
|
</figure>
|
||||||
|
}
|
||||||
112
code_neon_templ.go
Normal file
112
code_neon_templ.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// codeNeonComponent renders the cyberpunk:code_neon block.
|
||||||
|
func codeNeonComponent(data CodeNeonData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<figure data-block=\"cyberpunk:code_neon\" class=\"cyberpunk-code w-full my-6 rounded-lg overflow-hidden neon-edge-magenta\" style=\"background-color: hsl(var(--card)); color: hsl(var(--card-foreground));\"><figcaption class=\"flex items-center justify-between px-4 py-2 cyberpunk-mono text-xs uppercase tracking-widest\" style=\"background-color: hsl(var(--secondary)); color: hsl(var(--muted-foreground));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Language)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_neon.templ`, Line: 11, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Copy {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<button type=\"button\" class=\"cyberpunk-code-copy cyberpunk-mono uppercase tracking-widest px-2 py-1 rounded\" style=\"border: 1px solid hsl(var(--border)); color: hsl(var(--accent));\" data-copy=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Code)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_neon.templ`, Line: 17, Col: 26}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">COPY</button>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</figcaption><pre class=\"cyberpunk-mono overflow-x-auto p-4 text-sm leading-relaxed\" style=\"background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 = []any{"language-" + data.Language}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<code class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var4).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_neon.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Code)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_neon.templ`, Line: 23, Col: 284}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</code></pre></figure>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
41
cta_terminal.go
Normal file
41
cta_terminal.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CTATerminalMeta defines metadata for the cyberpunk:cta_terminal block.
|
||||||
|
var CTATerminalMeta = blocks.BlockMeta{
|
||||||
|
Key: "cta_terminal",
|
||||||
|
Title: "Terminal CTA",
|
||||||
|
Description: "Faux $ prompt with blinking caret and copy button",
|
||||||
|
Category: blocks.CategoryLayout,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// CTATerminalData is the typed view of the cta_terminal content map.
|
||||||
|
type CTATerminalData struct {
|
||||||
|
Prompt string
|
||||||
|
Button string
|
||||||
|
Href string
|
||||||
|
Copyable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CTATerminalBlock renders the cta_terminal block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {prompt, button, href, copyable: bool|"true"|"false"}
|
||||||
|
func CTATerminalBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := CTATerminalData{
|
||||||
|
Prompt: getStringWithDefault(content, "prompt", "$ npm i your-thing"),
|
||||||
|
Button: getStringWithDefault(content, "button", "Copy"),
|
||||||
|
Href: getString(content, "href"),
|
||||||
|
Copyable: getBool(content, "copyable", true),
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = ctaTerminalComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
37
cta_terminal.templ
Normal file
37
cta_terminal.templ
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// ctaTerminalComponent renders the cyberpunk:cta_terminal block as a faux terminal prompt.
|
||||||
|
templ ctaTerminalComponent(data CTATerminalData) {
|
||||||
|
<section
|
||||||
|
data-block="cyberpunk:cta_terminal"
|
||||||
|
class="cyberpunk-cta-terminal w-full py-16"
|
||||||
|
style="background-color: hsl(var(--card));"
|
||||||
|
>
|
||||||
|
<div class="max-w-4xl mx-auto px-4">
|
||||||
|
<div
|
||||||
|
class="cyberpunk-mono rounded-lg p-6 md:p-8 flex flex-col md:flex-row items-stretch md:items-center gap-4"
|
||||||
|
style="background-color: hsl(var(--background)); border: 1px dashed hsl(var(--primary));"
|
||||||
|
>
|
||||||
|
<div class="flex-1 flex items-center gap-2 text-base md:text-lg overflow-hidden">
|
||||||
|
<span aria-hidden="true" style="color: hsl(var(--accent));">$</span>
|
||||||
|
<code class="cyberpunk-mono whitespace-pre-wrap break-all" style="color: hsl(var(--foreground));" data-caret-blink>
|
||||||
|
{ data.Prompt }
|
||||||
|
</code>
|
||||||
|
<span aria-hidden="true" class="caret-blink" data-caret-blink style="color: hsl(var(--primary));">▍</span>
|
||||||
|
</div>
|
||||||
|
if data.Button != "" {
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(safeHref(data.Href)) }
|
||||||
|
class="cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center justify-center gap-2 px-4 py-2 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));"
|
||||||
|
if data.Copyable {
|
||||||
|
data-copy={ data.Prompt }
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ data.Button }
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
113
cta_terminal_templ.go
Normal file
113
cta_terminal_templ.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// ctaTerminalComponent renders the cyberpunk:cta_terminal block as a faux terminal prompt.
|
||||||
|
func ctaTerminalComponent(data CTATerminalData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<section data-block=\"cyberpunk:cta_terminal\" class=\"cyberpunk-cta-terminal w-full py-16\" style=\"background-color: hsl(var(--card));\"><div class=\"max-w-4xl mx-auto px-4\"><div class=\"cyberpunk-mono rounded-lg p-6 md:p-8 flex flex-col md:flex-row items-stretch md:items-center gap-4\" style=\"background-color: hsl(var(--background)); border: 1px dashed hsl(var(--primary));\"><div class=\"flex-1 flex items-center gap-2 text-base md:text-lg overflow-hidden\"><span aria-hidden=\"true\" style=\"color: hsl(var(--accent));\">$</span> <code class=\"cyberpunk-mono whitespace-pre-wrap break-all\" style=\"color: hsl(var(--foreground));\" data-caret-blink>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Prompt)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cta_terminal.templ`, Line: 18, Col: 19}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</code> <span aria-hidden=\"true\" class=\"caret-blink\" data-caret-blink style=\"color: hsl(var(--primary));\">▍</span></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Button != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(safeHref(data.Href)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cta_terminal.templ`, Line: 24, Col: 47}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center justify-center gap-2 px-4 py-2 rounded-md font-semibold uppercase tracking-wider\" style=\"background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Copyable {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " data-copy=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Prompt)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cta_terminal.templ`, Line: 28, Col: 30}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Button)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `cta_terminal.templ`, Line: 31, Col: 19}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
102
email_wrapper.templ
Normal file
102
email_wrapper.templ
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkEmailWrapper is the registered cyberpunk:email_wrapper.
|
||||||
|
// It produces a dark email with a mono preheader, magenta hairline divider,
|
||||||
|
// and a literal "[esc] unsubscribe" link per UAT §10.
|
||||||
|
//
|
||||||
|
// UAT §10 mandates `body { background:#0a0a12 }`. That literal hex lives in the
|
||||||
|
// <style> head block (a <style> element is not a `style=` attribute and so does
|
||||||
|
// not trip the no-inline-hex-style rule). All other colour values use hsl()
|
||||||
|
// notation, which most modern mail clients render fine.
|
||||||
|
func CyberpunkEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkEmailTemplate is the Cyberpunk-branded HTML email shell.
|
||||||
|
templ cyberpunkEmailTemplate(emailCtx templates.EmailContext, body string) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<meta name="x-apple-disable-message-reformatting"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
|
<title>{ emailCtx.SiteSettings.SiteName }</title>
|
||||||
|
<style type="text/css">
|
||||||
|
body { background:#0a0a12; margin:0; padding:0; color: hsl(260 25% 92%); }
|
||||||
|
table, td { mso-table-lspace:0pt; mso-table-rspace:0pt; }
|
||||||
|
a { color: inherit; }
|
||||||
|
.cyberpunk-preheader { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||||
|
.cyberpunk-divider { border-top:1px solid hsl(320 100% 60%); }
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
.email-container { width:100% !important; max-width:100% !important; }
|
||||||
|
.content-padding { padding-left:24px !important; padding-right:24px !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body bgcolor="#0a0a12" style="margin:0;padding:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">
|
||||||
|
if emailCtx.PreviewText != "" {
|
||||||
|
<div class="cyberpunk-preheader" style="display:none;max-height:0;overflow:hidden;mso-hide:all;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;">
|
||||||
|
$ from: { emailCtx.SiteSettings.SiteName } — { emailCtx.PreviewText }
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<div class="cyberpunk-preheader" style="display:none;max-height:0;overflow:hidden;mso-hide:all;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;">
|
||||||
|
$ from: { emailCtx.SiteSettings.SiteName }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#0a0a12">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:48px 12px;">
|
||||||
|
<table role="presentation" class="email-container" width="600" cellpadding="0" cellspacing="0" border="0" bgcolor="#101020" style="max-width:600px;border:1px solid hsl(258 28% 20%);">
|
||||||
|
<tr>
|
||||||
|
<td class="content-padding" align="center" style="padding:32px 40px;border-bottom:1px solid hsl(320 100% 60%);">
|
||||||
|
<div class="cyberpunk-preheader" style="font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;letter-spacing:0.2em;text-transform:uppercase;">
|
||||||
|
$ from: { emailCtx.SiteSettings.SiteName }
|
||||||
|
</div>
|
||||||
|
if emailCtx.SiteSettings.LogoURL != "" {
|
||||||
|
<img src={ emailCtx.SiteSettings.LogoURL } alt={ emailCtx.SiteSettings.SiteName } style="margin-top:12px;max-height:48px;width:auto;display:inline-block;"/>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="content-padding" style="padding:40px 48px;font-size:16px;line-height:1.7;">
|
||||||
|
@templ.Raw(body)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="cyberpunk-divider" height="1" style="border-top:1px solid hsl(320 100% 60%);font-size:0;line-height:0;"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="content-padding" align="center" style="padding:24px 48px 40px;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;letter-spacing:0.15em;text-transform:uppercase;">
|
||||||
|
if emailCtx.SiteSettings.SiteURL != "" {
|
||||||
|
<p style="margin:0 0 12px;">
|
||||||
|
<a href={ templ.SafeURL(emailCtx.SiteSettings.SiteURL) } style="text-decoration:none;">
|
||||||
|
{ emailCtx.SiteSettings.SiteURL }
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
if emailCtx.UnsubscribeURL != "" {
|
||||||
|
<p style="margin:0;">
|
||||||
|
<a href={ templ.SafeURL(emailCtx.UnsubscribeURL) } style="text-decoration:none;">[esc] unsubscribe</a>
|
||||||
|
</p>
|
||||||
|
} else {
|
||||||
|
<p style="margin:0;">[esc] unsubscribe</p>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
246
email_wrapper_templ.go
Normal file
246
email_wrapper_templ.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkEmailWrapper is the registered cyberpunk:email_wrapper.
|
||||||
|
// It produces a dark email with a mono preheader, magenta hairline divider,
|
||||||
|
// and a literal "[esc] unsubscribe" link per UAT §10.
|
||||||
|
//
|
||||||
|
// UAT §10 mandates `body { background:#0a0a12 }`. That literal hex lives in the
|
||||||
|
// <style> head block (a <style> element is not a `style=` attribute and so does
|
||||||
|
// not trip the no-inline-hex-style rule). All other colour values use hsl()
|
||||||
|
// notation, which most modern mail clients render fine.
|
||||||
|
func CyberpunkEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkEmailTemplate is the Cyberpunk-branded HTML email shell.
|
||||||
|
func cyberpunkEmailTemplate(emailCtx templates.EmailContext, body string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"x-apple-disable-message-reformatting\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><title>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 33, Col: 42}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title><style type=\"text/css\">\n\t\t\t\tbody { background:#0a0a12; margin:0; padding:0; color: hsl(260 25% 92%); }\n\t\t\t\ttable, td { mso-table-lspace:0pt; mso-table-rspace:0pt; }\n\t\t\t\ta { color: inherit; }\n\t\t\t\t.cyberpunk-preheader { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }\n\t\t\t\t.cyberpunk-divider { border-top:1px solid hsl(320 100% 60%); }\n\t\t\t\t@media only screen and (max-width: 620px) {\n\t\t\t\t\t.email-container { width:100% !important; max-width:100% !important; }\n\t\t\t\t\t.content-padding { padding-left:24px !important; padding-right:24px !important; }\n\t\t\t\t}\n\t\t\t</style></head><body bgcolor=\"#0a0a12\" style=\"margin:0;padding:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if emailCtx.PreviewText != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"cyberpunk-preheader\" style=\"display:none;max-height:0;overflow:hidden;mso-hide:all;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\">$ from: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 49, Col: 45}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " — ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.PreviewText)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 49, Col: 74}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"cyberpunk-preheader\" style=\"display:none;max-height:0;overflow:hidden;mso-hide:all;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\">$ from: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 53, Col: 45}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" bgcolor=\"#0a0a12\"><tr><td align=\"center\" style=\"padding:48px 12px;\"><table role=\"presentation\" class=\"email-container\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" bgcolor=\"#101020\" style=\"max-width:600px;border:1px solid hsl(258 28% 20%);\"><tr><td class=\"content-padding\" align=\"center\" style=\"padding:32px 40px;border-bottom:1px solid hsl(320 100% 60%);\"><div class=\"cyberpunk-preheader\" style=\"font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;letter-spacing:0.2em;text-transform:uppercase;\">$ from: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 63, Col: 50}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.LogoURL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(emailCtx.SiteSettings.LogoURL)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 66, Col: 50}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" alt=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(emailCtx.SiteSettings.SiteName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 66, Col: 89}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" style=\"margin-top:12px;max-height:48px;width:auto;display:inline-block;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</td></tr><tr><td class=\"content-padding\" style=\"padding:40px 48px;font-size:16px;line-height:1.7;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(body).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</td></tr><tr><td class=\"cyberpunk-divider\" height=\"1\" style=\"border-top:1px solid hsl(320 100% 60%);font-size:0;line-height:0;\"> </td></tr><tr><td class=\"content-padding\" align=\"center\" style=\"padding:24px 48px 40px;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;letter-spacing:0.15em;text-transform:uppercase;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if emailCtx.SiteSettings.SiteURL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<p style=\"margin:0 0 12px;\"><a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(emailCtx.SiteSettings.SiteURL))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 82, Col: 65}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" style=\"text-decoration:none;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(emailCtx.SiteSettings.SiteURL)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 83, Col: 43}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</a></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if emailCtx.UnsubscribeURL != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<p style=\"margin:0;\"><a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(emailCtx.UnsubscribeURL))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `email_wrapper.templ`, Line: 89, Col: 59}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" style=\"text-decoration:none;\">[esc] unsubscribe</a></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<p style=\"margin:0;\">[esc] unsubscribe</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</td></tr></table></td></tr></table></body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
60
embed.go
Normal file
60
embed.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed assets/*
|
||||||
|
var assetsFS embed.FS
|
||||||
|
|
||||||
|
//go:embed schemas/*
|
||||||
|
var schemasFS embed.FS
|
||||||
|
|
||||||
|
//go:embed presets.json
|
||||||
|
var presetsData []byte
|
||||||
|
|
||||||
|
//go:embed fonts.json
|
||||||
|
var fontsData []byte
|
||||||
|
|
||||||
|
//go:embed plugin.mod
|
||||||
|
var pluginModBytes []byte
|
||||||
|
|
||||||
|
// Assets returns the embedded assets filesystem (rooted at "assets/").
|
||||||
|
func Assets() fs.FS {
|
||||||
|
sub, _ := fs.Sub(assetsFS, "assets")
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schemas returns the embedded schemas filesystem (rooted at "schemas/").
|
||||||
|
func Schemas() fs.FS {
|
||||||
|
sub, _ := fs.Sub(schemasFS, "schemas")
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetsHandler returns an http.Handler that serves the embedded assets at /templates/cyberpunk/...
|
||||||
|
func AssetsHandler() http.Handler {
|
||||||
|
return http.FileServer(http.FS(Assets()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThemePresets returns the embedded theme presets JSON.
|
||||||
|
func ThemePresets() []byte { return presetsData }
|
||||||
|
|
||||||
|
// BundledFonts returns the embedded fonts manifest JSON.
|
||||||
|
func BundledFonts() []byte { return fontsData }
|
||||||
|
|
||||||
|
// ThemeCSSManifest exposes the theme's custom CSS (scanlines, glitch, neon-edge,
|
||||||
|
// caret-blink keyframes, font-family fallbacks) to the host Tailwind input so the
|
||||||
|
// utilities survive prod purging.
|
||||||
|
func ThemeCSSManifest() *plugin.CSSManifest {
|
||||||
|
css, err := assetsFS.ReadFile("assets/css/cyberpunk.css")
|
||||||
|
if err != nil {
|
||||||
|
return &plugin.CSSManifest{}
|
||||||
|
}
|
||||||
|
return &plugin.CSSManifest{
|
||||||
|
InputCSSAppend: string(css),
|
||||||
|
}
|
||||||
|
}
|
||||||
51
feature_card_neon.go
Normal file
51
feature_card_neon.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FeatureCardNeonMeta defines metadata for the cyberpunk:feature_card_neon block.
|
||||||
|
var FeatureCardNeonMeta = blocks.BlockMeta{
|
||||||
|
Key: "feature_card_neon",
|
||||||
|
Title: "Neon Feature Card",
|
||||||
|
Description: "Single feature card with per-card neon edge glow (magenta/cyan/lime)",
|
||||||
|
Category: blocks.CategoryLayout,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeatureCardNeonData is the typed view of the feature_card_neon content map.
|
||||||
|
type FeatureCardNeonData struct {
|
||||||
|
Icon string
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
Accent string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeatureCardNeonBlock renders the feature_card_neon block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {icon, title, body, accent:"magenta"|"cyan"|"lime"}
|
||||||
|
func FeatureCardNeonBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := FeatureCardNeonData{
|
||||||
|
Icon: getString(content, "icon"),
|
||||||
|
Title: getStringWithDefault(content, "title", "Feature"),
|
||||||
|
Body: getString(content, "body"),
|
||||||
|
Accent: parseAccent(getString(content, "accent")),
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = featureCardNeonComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseAccent normalises the accent value to one of the three known accents.
|
||||||
|
func parseAccent(v string) string {
|
||||||
|
switch v {
|
||||||
|
case "magenta", "cyan", "lime":
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
return "magenta"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
feature_card_neon.templ
Normal file
37
feature_card_neon.templ
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// neonEdgeClass returns the per-accent neon-edge utility class.
|
||||||
|
func neonEdgeClass(accent string) string {
|
||||||
|
switch accent {
|
||||||
|
case "cyan":
|
||||||
|
return "neon-edge-cyan"
|
||||||
|
case "lime":
|
||||||
|
return "neon-edge-lime"
|
||||||
|
default:
|
||||||
|
return "neon-edge-magenta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// featureCardNeonComponent renders the cyberpunk:feature_card_neon block.
|
||||||
|
templ featureCardNeonComponent(data FeatureCardNeonData) {
|
||||||
|
<article
|
||||||
|
data-block="cyberpunk:feature_card_neon"
|
||||||
|
data-accent={ data.Accent }
|
||||||
|
class={ "cyberpunk-feature-card p-6 rounded-lg h-full flex flex-col gap-4", neonEdgeClass(data.Accent) }
|
||||||
|
style="background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border));"
|
||||||
|
>
|
||||||
|
if data.Icon != "" {
|
||||||
|
<div class="cyberpunk-feature-icon w-10 h-10 flex items-center justify-center rounded-md" style="background-color: hsl(var(--secondary));">
|
||||||
|
<img src={ data.Icon } alt="" class="w-6 h-6" onerror="this.style.display='none'"/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<h3 class="cyberpunk-feature-title text-lg font-semibold" style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);">
|
||||||
|
{ data.Title }
|
||||||
|
</h3>
|
||||||
|
if data.Body != "" {
|
||||||
|
<div class="cyberpunk-feature-body text-sm leading-relaxed" style="color: hsl(var(--muted-foreground));">
|
||||||
|
@templ.Raw(data.Body)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</article>
|
||||||
|
}
|
||||||
138
feature_card_neon_templ.go
Normal file
138
feature_card_neon_templ.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// neonEdgeClass returns the per-accent neon-edge utility class.
|
||||||
|
func neonEdgeClass(accent string) string {
|
||||||
|
switch accent {
|
||||||
|
case "cyan":
|
||||||
|
return "neon-edge-cyan"
|
||||||
|
case "lime":
|
||||||
|
return "neon-edge-lime"
|
||||||
|
default:
|
||||||
|
return "neon-edge-magenta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// featureCardNeonComponent renders the cyberpunk:feature_card_neon block.
|
||||||
|
func featureCardNeonComponent(data FeatureCardNeonData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
var templ_7745c5c3_Var2 = []any{"cyberpunk-feature-card p-6 rounded-lg h-full flex flex-col gap-4", neonEdgeClass(data.Accent)}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<article data-block=\"cyberpunk:feature_card_neon\" data-accent=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Accent)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `feature_card_neon.templ`, Line: 19, Col: 27}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `feature_card_neon.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" style=\"background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Icon != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"cyberpunk-feature-icon w-10 h-10 flex items-center justify-center rounded-md\" style=\"background-color: hsl(var(--secondary));\"><img src=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Icon)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `feature_card_neon.templ`, Line: 25, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" alt=\"\" class=\"w-6 h-6\" onerror=\"this.style.display='none'\"></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<h3 class=\"cyberpunk-feature-title text-lg font-semibold\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `feature_card_neon.templ`, Line: 29, Col: 15}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</h3>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Body != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div class=\"cyberpunk-feature-body text-sm leading-relaxed\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Body).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</article>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
1
fonts.json
Normal file
1
fonts.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
62
footer_grid.go
Normal file
62
footer_grid.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FooterGridMeta defines metadata for the cyberpunk:footer_grid block.
|
||||||
|
var FooterGridMeta = blocks.BlockMeta{
|
||||||
|
Key: "footer_grid",
|
||||||
|
Title: "Grid Footer",
|
||||||
|
Description: "Mono columns, build-hash + uptime status pill",
|
||||||
|
Category: blocks.CategoryNavigation,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterColumn is one column of footer links.
|
||||||
|
type FooterColumn struct {
|
||||||
|
Title string
|
||||||
|
Links []Link
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterGridData is the typed view of the footer_grid content map.
|
||||||
|
type FooterGridData struct {
|
||||||
|
ShowStatus bool
|
||||||
|
BuildHash string
|
||||||
|
Columns []FooterColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
// FooterGridBlock renders the footer_grid block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {showStatus, buildHash, columns:[{title, links:[{label, href}]}]}
|
||||||
|
func FooterGridBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
cols := getSlice(content, "columns")
|
||||||
|
columns := make([]FooterColumn, 0, len(cols))
|
||||||
|
for _, col := range cols {
|
||||||
|
linkObjs := getSlice(col, "links")
|
||||||
|
links := make([]Link, 0, len(linkObjs))
|
||||||
|
for _, l := range linkObjs {
|
||||||
|
links = append(links, Link{
|
||||||
|
Label: getString(l, "label"),
|
||||||
|
Href: getString(l, "href"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
columns = append(columns, FooterColumn{
|
||||||
|
Title: getString(col, "title"),
|
||||||
|
Links: links,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data := FooterGridData{
|
||||||
|
ShowStatus: getBool(content, "showStatus", true),
|
||||||
|
BuildHash: getStringWithDefault(content, "buildHash", "dev"),
|
||||||
|
Columns: columns,
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = footerGridComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
77
footer_grid.templ
Normal file
77
footer_grid.templ
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// footerGridColumns returns a tailwind grid-column class for n columns.
|
||||||
|
func footerGridColumns(n int) string {
|
||||||
|
switch n {
|
||||||
|
case 0, 1:
|
||||||
|
return "grid-cols-1"
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 3:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// footerGridComponent renders the cyberpunk:footer_grid block.
|
||||||
|
templ footerGridComponent(data FooterGridData) {
|
||||||
|
<div
|
||||||
|
data-block="cyberpunk:footer_grid"
|
||||||
|
class="cyberpunk-footer w-full"
|
||||||
|
style="background-color: hsl(var(--card)); color: hsl(var(--muted-foreground));"
|
||||||
|
>
|
||||||
|
<div class="max-w-6xl mx-auto px-4 py-12">
|
||||||
|
if len(data.Columns) > 0 {
|
||||||
|
<div class={ "grid gap-8 mb-10 cyberpunk-mono text-sm", footerGridColumns(len(data.Columns)) }>
|
||||||
|
for _, col := range data.Columns {
|
||||||
|
<div>
|
||||||
|
if col.Title != "" {
|
||||||
|
<p class="cyberpunk-mono uppercase tracking-widest text-xs mb-4" style="color: hsl(var(--accent));">
|
||||||
|
{ col.Title }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
if len(col.Links) > 0 {
|
||||||
|
<ul class="space-y-2">
|
||||||
|
for _, link := range col.Links {
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(safeHref(link.Href)) }
|
||||||
|
class="cyberpunk-footer-link hover:underline"
|
||||||
|
style="color: hsl(var(--foreground));"
|
||||||
|
>
|
||||||
|
{ link.Label }
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<p class="cyberpunk-mono mb-6 text-xs uppercase tracking-widest" style="color: hsl(var(--muted-foreground));">
|
||||||
|
{ "// add columns to populate this grid" }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-3 pt-6" style="border-top: 1px dashed hsl(var(--border));">
|
||||||
|
<p class="cyberpunk-mono text-xs uppercase tracking-widest" style="color: hsl(var(--muted-foreground));">
|
||||||
|
BUILD <span style="color: hsl(var(--foreground));">{ data.BuildHash }</span>
|
||||||
|
</p>
|
||||||
|
if data.ShowStatus {
|
||||||
|
<span
|
||||||
|
class="cyberpunk-status-pill cyberpunk-mono inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs uppercase tracking-widest"
|
||||||
|
style="border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="cyberpunk-status-dot inline-block w-2 h-2 rounded-full"
|
||||||
|
style="background-color: hsl(var(--accent)); box-shadow: 0 0 6px hsl(var(--accent));"
|
||||||
|
></span>
|
||||||
|
OK
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
199
footer_grid_templ.go
Normal file
199
footer_grid_templ.go
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// footerGridColumns returns a tailwind grid-column class for n columns.
|
||||||
|
func footerGridColumns(n int) string {
|
||||||
|
switch n {
|
||||||
|
case 0, 1:
|
||||||
|
return "grid-cols-1"
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 3:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// footerGridComponent renders the cyberpunk:footer_grid block.
|
||||||
|
func footerGridComponent(data FooterGridData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div data-block=\"cyberpunk:footer_grid\" class=\"cyberpunk-footer w-full\" style=\"background-color: hsl(var(--card)); color: hsl(var(--muted-foreground));\"><div class=\"max-w-6xl mx-auto px-4 py-12\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if len(data.Columns) > 0 {
|
||||||
|
var templ_7745c5c3_Var2 = []any{"grid gap-8 mb-10 cyberpunk-mono text-sm", footerGridColumns(len(data.Columns))}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, col := range data.Columns {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if col.Title != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"cyberpunk-mono uppercase tracking-widest text-xs mb-4\" style=\"color: hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(col.Title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 31, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(col.Links) > 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<ul class=\"space-y-2\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, link := range col.Links {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<li><a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(safeHref(link.Href)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 39, Col: 53}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" class=\"cyberpunk-footer-link hover:underline\" style=\"color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(link.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 43, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</a></li>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ul>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<p class=\"cyberpunk-mono mb-6 text-xs uppercase tracking-widest\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs("// add columns to populate this grid")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 54, Col: 45}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div class=\"flex flex-col md:flex-row items-start md:items-center justify-between gap-3 pt-6\" style=\"border-top: 1px dashed hsl(var(--border));\"><p class=\"cyberpunk-mono text-xs uppercase tracking-widest\" style=\"color: hsl(var(--muted-foreground));\">BUILD <span style=\"color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.BuildHash)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `footer_grid.templ`, Line: 59, Col: 72}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</span></p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.ShowStatus {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<span class=\"cyberpunk-status-pill cyberpunk-mono inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs uppercase tracking-widest\" style=\"border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));\"><span aria-hidden=\"true\" class=\"cyberpunk-status-dot inline-block w-2 h-2 rounded-full\" style=\"background-color: hsl(var(--accent)); box-shadow: 0 0 6px hsl(var(--accent));\"></span> OK</span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module git.dev.alexdunmow.com/block/themes/cyberpunk
|
||||||
|
|
||||||
|
go 1.26.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.dev.alexdunmow.com/block/core v0.11.1
|
||||||
|
github.com/a-h/templ v0.3.1020
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
connectrpc.com/connect v1.20.0 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
||||||
|
golang.org/x/mod v0.34.0 // indirect
|
||||||
|
golang.org/x/text v0.36.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
)
|
||||||
42
go.sum
Normal file
42
go.sum
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
|
||||||
|
connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
||||||
|
git.dev.alexdunmow.com/block/core v0.11.1 h1:5b3Ps9CLor2FGyxw/Qovt27AGZKR5Xi1JZGi/TfliTA=
|
||||||
|
git.dev.alexdunmow.com/block/core v0.11.1/go.mod h1:ZwzEOxRDLDfrhQGqo6hLw01/C1z/aS4Dm9ljQMl0Bg4=
|
||||||
|
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||||
|
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
||||||
|
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||||
|
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||||
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||||
|
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
18
heading_override.go
Normal file
18
heading_override.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkHeadingBlock renders the built-in heading block with cyberpunk styling.
|
||||||
|
// H1/H2 get the glitch text-shadow (magenta + cyan offsets); smaller levels get
|
||||||
|
// mono small-caps eyebrow class.
|
||||||
|
func CyberpunkHeadingBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
text := getString(content, "text")
|
||||||
|
textClass := getString(content, "textClass")
|
||||||
|
level := parseHeadingLevel(content)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkHeadingComponent(level, text, textClass).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
81
heading_override.templ
Normal file
81
heading_override.templ
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// cyberpunkHeadingBaseClass returns default Tailwind classes for each heading level.
|
||||||
|
func cyberpunkHeadingBaseClass(level int) string {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return "text-4xl md:text-5xl font-bold tracking-tight"
|
||||||
|
case 2:
|
||||||
|
return "text-3xl md:text-4xl font-semibold tracking-tight"
|
||||||
|
case 3:
|
||||||
|
return "text-2xl font-semibold"
|
||||||
|
case 4:
|
||||||
|
return "text-xl font-semibold"
|
||||||
|
case 5:
|
||||||
|
return "text-lg font-medium"
|
||||||
|
case 6:
|
||||||
|
return "text-base font-medium"
|
||||||
|
default:
|
||||||
|
return "text-3xl font-semibold tracking-tight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkHeadingComponent renders the override for the built-in heading block.
|
||||||
|
// H1 and H2 get the .glitch class which gives them the RGB-split text-shadow.
|
||||||
|
// H3-H6 get the mono small-caps eyebrow class.
|
||||||
|
templ cyberpunkHeadingComponent(level int, text, textClass string) {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
<h1
|
||||||
|
class={ "cyberpunk-display glitch", cyberpunkHeadingBaseClass(1), textClass }
|
||||||
|
data-text={ text }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h1>
|
||||||
|
case 2:
|
||||||
|
<h2
|
||||||
|
class={ "cyberpunk-display glitch", cyberpunkHeadingBaseClass(2), textClass }
|
||||||
|
data-text={ text }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h2>
|
||||||
|
case 3:
|
||||||
|
<h3
|
||||||
|
class={ "cyberpunk-display", cyberpunkHeadingBaseClass(3), textClass }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h3>
|
||||||
|
case 4:
|
||||||
|
<h4
|
||||||
|
class={ "cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(4), textClass }
|
||||||
|
style="color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h4>
|
||||||
|
case 5:
|
||||||
|
<h5
|
||||||
|
class={ "cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(5), textClass }
|
||||||
|
style="color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h5>
|
||||||
|
case 6:
|
||||||
|
<h6
|
||||||
|
class={ "cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(6), textClass }
|
||||||
|
style="color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h6>
|
||||||
|
default:
|
||||||
|
<h2
|
||||||
|
class={ "cyberpunk-display glitch", cyberpunkHeadingBaseClass(2), textClass }
|
||||||
|
data-text={ text }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
{ text }
|
||||||
|
</h2>
|
||||||
|
}
|
||||||
|
}
|
||||||
352
heading_override_templ.go
Normal file
352
heading_override_templ.go
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// cyberpunkHeadingBaseClass returns default Tailwind classes for each heading level.
|
||||||
|
func cyberpunkHeadingBaseClass(level int) string {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return "text-4xl md:text-5xl font-bold tracking-tight"
|
||||||
|
case 2:
|
||||||
|
return "text-3xl md:text-4xl font-semibold tracking-tight"
|
||||||
|
case 3:
|
||||||
|
return "text-2xl font-semibold"
|
||||||
|
case 4:
|
||||||
|
return "text-xl font-semibold"
|
||||||
|
case 5:
|
||||||
|
return "text-lg font-medium"
|
||||||
|
case 6:
|
||||||
|
return "text-base font-medium"
|
||||||
|
default:
|
||||||
|
return "text-3xl font-semibold tracking-tight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cyberpunkHeadingComponent renders the override for the built-in heading block.
|
||||||
|
// H1 and H2 get the .glitch class which gives them the RGB-split text-shadow.
|
||||||
|
// H3-H6 get the mono small-caps eyebrow class.
|
||||||
|
func cyberpunkHeadingComponent(level int, text, textClass string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
var templ_7745c5c3_Var2 = []any{"cyberpunk-display glitch", cyberpunkHeadingBaseClass(1), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" data-text=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 31, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 34, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
var templ_7745c5c3_Var6 = []any{"cyberpunk-display glitch", cyberpunkHeadingBaseClass(2), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<h2 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var6).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" data-text=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 39, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 string
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 42, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</h2>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
var templ_7745c5c3_Var10 = []any{"cyberpunk-display", cyberpunkHeadingBaseClass(3), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<h3 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 string
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var10).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 49, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</h3>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
var templ_7745c5c3_Var13 = []any{"cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(4), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<h4 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var14 string
|
||||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var13).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" style=\"color: hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 56, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</h4>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
var templ_7745c5c3_Var16 = []any{"cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(5), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<h5 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var17 string
|
||||||
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var16).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" style=\"color: hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var18 string
|
||||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 63, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</h5>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 6:
|
||||||
|
var templ_7745c5c3_Var19 = []any{"cyberpunk-eyebrow cyberpunk-mono uppercase tracking-widest", cyberpunkHeadingBaseClass(6), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var19...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<h6 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var20 string
|
||||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var19).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" style=\"color: hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var21 string
|
||||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 70, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</h6>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var templ_7745c5c3_Var22 = []any{"cyberpunk-display glitch", cyberpunkHeadingBaseClass(2), textClass}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var22...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<h2 class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var23 string
|
||||||
|
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var22).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" data-text=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var24 string
|
||||||
|
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 75, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var25 string
|
||||||
|
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `heading_override.templ`, Line: 78, Col: 10}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</h2>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
102
helpers.go
Normal file
102
helpers.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// getString extracts a string value from a content map. Returns "" on miss/wrong-type.
|
||||||
|
func getString(content map[string]any, key string) string {
|
||||||
|
if v, ok := content[key].(string); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStringWithDefault is like getString but returns dflt if the key is missing or non-string.
|
||||||
|
func getStringWithDefault(content map[string]any, key, dflt string) string {
|
||||||
|
if v, ok := content[key].(string); ok && v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return dflt
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBool extracts a boolean value from a content map. Accepts bool, "true"/"false",
|
||||||
|
// "on"/"off", "yes"/"no", and numerics. Returns dflt when missing or unparsable.
|
||||||
|
func getBool(content map[string]any, key string, dflt bool) bool {
|
||||||
|
switch v := content[key].(type) {
|
||||||
|
case bool:
|
||||||
|
return v
|
||||||
|
case string:
|
||||||
|
switch v {
|
||||||
|
case "true", "on", "yes", "1":
|
||||||
|
return true
|
||||||
|
case "false", "off", "no", "0", "":
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
return v != 0
|
||||||
|
case int:
|
||||||
|
return v != 0
|
||||||
|
}
|
||||||
|
return dflt
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSlice extracts a slice of object-shaped entries from a content map.
|
||||||
|
func getSlice(content map[string]any, key string) []map[string]any {
|
||||||
|
v, ok := content[key].([]any)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]map[string]any, 0, len(v))
|
||||||
|
for _, item := range v {
|
||||||
|
if m, ok := item.(map[string]any); ok {
|
||||||
|
out = append(out, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLink reads a {label, href} link object. Tolerates string values
|
||||||
|
// (interpreted as href, label = href) and missing keys.
|
||||||
|
func getLink(content map[string]any, key string) Link {
|
||||||
|
v := content[key]
|
||||||
|
switch x := v.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
return Link{Label: getString(x, "label"), Href: getString(x, "href")}
|
||||||
|
case string:
|
||||||
|
return Link{Label: x, Href: x}
|
||||||
|
}
|
||||||
|
return Link{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link is a {label, href} pair used by hero / nav / footer blocks.
|
||||||
|
type Link struct {
|
||||||
|
Label string
|
||||||
|
Href string
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeHref returns "#" when href is empty so templ.SafeURL never receives "".
|
||||||
|
func safeHref(href string) string {
|
||||||
|
if href == "" {
|
||||||
|
return "#"
|
||||||
|
}
|
||||||
|
return href
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHeadingLevel parses a 1..6 heading level. Defaults to 2 for safety.
|
||||||
|
func parseHeadingLevel(content map[string]any) int {
|
||||||
|
switch v := content["level"].(type) {
|
||||||
|
case float64:
|
||||||
|
l := int(v)
|
||||||
|
if l >= 1 && l <= 6 {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if v >= 1 && v <= 6 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
if l, err := strconv.Atoi(v); err == nil && l >= 1 && l <= 6 {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
55
hero_glitch.go
Normal file
55
hero_glitch.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeroGlitchMeta defines metadata for the cyberpunk glitch hero block.
|
||||||
|
var HeroGlitchMeta = blocks.BlockMeta{
|
||||||
|
Key: "hero_glitch",
|
||||||
|
Title: "Glitch Hero",
|
||||||
|
Description: "RGB-split H1, neon CTA, optional scanline overlay toggle",
|
||||||
|
Category: blocks.CategoryLayout,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeroGlitchData is the typed view of the hero_glitch content map.
|
||||||
|
type HeroGlitchData struct {
|
||||||
|
Eyebrow string
|
||||||
|
Headline string
|
||||||
|
Sub string
|
||||||
|
CTA Link
|
||||||
|
SecondaryCTA Link
|
||||||
|
Scanlines bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeroGlitchBlock renders the hero_glitch block.
|
||||||
|
//
|
||||||
|
// Content shape (matches schemas/hero_glitch.schema.json):
|
||||||
|
// {eyebrow, headline, sub, cta:{label,href}, secondaryCta:{label,href}, scanlines:"on"|"off"}
|
||||||
|
func HeroGlitchBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := HeroGlitchData{
|
||||||
|
Eyebrow: getString(content, "eyebrow"),
|
||||||
|
Headline: getStringWithDefault(content, "headline", "// hero headline"),
|
||||||
|
Sub: getString(content, "sub"),
|
||||||
|
CTA: getLink(content, "cta"),
|
||||||
|
SecondaryCTA: getLink(content, "secondaryCta"),
|
||||||
|
Scanlines: parseScanlines(content),
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = heroGlitchComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScanlines(content map[string]any) bool {
|
||||||
|
if v, ok := content["scanlines"].(string); ok {
|
||||||
|
return v == "on" || v == "true"
|
||||||
|
}
|
||||||
|
if v, ok := content["scanlines"].(bool); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
54
hero_glitch.templ
Normal file
54
hero_glitch.templ
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// heroGlitchComponent renders the cyberpunk:hero_glitch block with an RGB-split H1.
|
||||||
|
templ heroGlitchComponent(data HeroGlitchData) {
|
||||||
|
<section
|
||||||
|
data-block="cyberpunk:hero_glitch"
|
||||||
|
class="cyberpunk-hero relative w-full overflow-hidden py-24 md:py-32"
|
||||||
|
style="background-color: hsl(var(--background));"
|
||||||
|
>
|
||||||
|
if data.Scanlines {
|
||||||
|
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0" data-scanlines-local></div>
|
||||||
|
}
|
||||||
|
<div class="relative z-10 max-w-5xl mx-auto px-4 text-center">
|
||||||
|
if data.Eyebrow != "" {
|
||||||
|
<p class="cyberpunk-eyebrow cyberpunk-mono inline-block px-3 py-1 mb-6 text-xs uppercase tracking-[0.35em]" style="color: hsl(var(--accent)); border: 1px dashed hsl(var(--accent));">
|
||||||
|
{ data.Eyebrow }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<h1
|
||||||
|
class="glitch cyberpunk-display text-5xl md:text-7xl font-bold leading-[1.05] tracking-tight mb-8"
|
||||||
|
data-text={ data.Headline }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
{ data.Headline }
|
||||||
|
</h1>
|
||||||
|
if data.Sub != "" {
|
||||||
|
<p class="cyberpunk-sub mx-auto max-w-2xl text-lg md:text-xl mb-10" style="color: hsl(var(--muted-foreground));">
|
||||||
|
{ data.Sub }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||||
|
if data.CTA.Label != "" {
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(safeHref(data.CTA.Href)) }
|
||||||
|
class="cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center gap-2 px-6 py-3 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));"
|
||||||
|
>
|
||||||
|
<span>{ data.CTA.Label }</span>
|
||||||
|
<span aria-hidden="true" class="caret-blink">_</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
if data.SecondaryCTA.Label != "" {
|
||||||
|
<a
|
||||||
|
href={ templ.SafeURL(safeHref(data.SecondaryCTA.Href)) }
|
||||||
|
class="cyberpunk-btn-ghost cyberpunk-mono inline-flex items-center gap-2 px-6 py-3 rounded-md font-semibold uppercase tracking-wider"
|
||||||
|
style="border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));"
|
||||||
|
>
|
||||||
|
<span>{ data.SecondaryCTA.Label }</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
191
hero_glitch_templ.go
Normal file
191
hero_glitch_templ.go
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// heroGlitchComponent renders the cyberpunk:hero_glitch block with an RGB-split H1.
|
||||||
|
func heroGlitchComponent(data HeroGlitchData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<section data-block=\"cyberpunk:hero_glitch\" class=\"cyberpunk-hero relative w-full overflow-hidden py-24 md:py-32\" style=\"background-color: hsl(var(--background));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Scanlines {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div aria-hidden=\"true\" class=\"scanlines pointer-events-none absolute inset-0\" data-scanlines-local></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"relative z-10 max-w-5xl mx-auto px-4 text-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Eyebrow != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<p class=\"cyberpunk-eyebrow cyberpunk-mono inline-block px-3 py-1 mb-6 text-xs uppercase tracking-[0.35em]\" style=\"color: hsl(var(--accent)); border: 1px dashed hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Eyebrow)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 16, Col: 19}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<h1 class=\"glitch cyberpunk-display text-5xl md:text-7xl font-bold leading-[1.05] tracking-tight mb-8\" data-text=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Headline)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 21, Col: 29}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-heading, 'Space Grotesk', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Headline)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 24, Col: 19}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</h1>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.Sub != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"cyberpunk-sub mx-auto max-w-2xl text-lg md:text-xl mb-10\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Sub)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 28, Col: 15}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"flex flex-col sm:flex-row gap-4 justify-center items-center\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.CTA.Label != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(safeHref(data.CTA.Href)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 34, Col: 51}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"cyberpunk-btn rgb-split cyberpunk-mono inline-flex items-center gap-2 px-6 py-3 rounded-md font-semibold uppercase tracking-wider\" style=\"background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.CTA.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 38, Col: 28}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</span> <span aria-hidden=\"true\" class=\"caret-blink\">_</span></a> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.SecondaryCTA.Label != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<a href=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 templ.SafeURL
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(safeHref(data.SecondaryCTA.Href)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 44, Col: 60}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"cyberpunk-btn-ghost cyberpunk-mono inline-flex items-center gap-2 px-6 py-3 rounded-md font-semibold uppercase tracking-wider\" style=\"border: 1px solid hsl(var(--accent)); color: hsl(var(--accent));\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 string
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(data.SecondaryCTA.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `hero_glitch.templ`, Line: 48, Col: 37}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</span></a>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div></div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
43
navbar_terminal.go
Normal file
43
navbar_terminal.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NavbarTerminalMeta defines metadata for the cyberpunk:navbar_terminal block.
|
||||||
|
var NavbarTerminalMeta = blocks.BlockMeta{
|
||||||
|
Key: "navbar_terminal",
|
||||||
|
Title: "Terminal Navbar",
|
||||||
|
Description: "Mono-spaced navbar with $ command prefix and optional status dot",
|
||||||
|
Category: blocks.CategoryNavigation,
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavbarTerminalData is the typed view of the navbar_terminal content map.
|
||||||
|
type NavbarTerminalData struct {
|
||||||
|
MenuName string
|
||||||
|
CommandPrefix string
|
||||||
|
StatusDot bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavbarTerminalBlock renders the navbar_terminal block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {menuName, commandPrefix, statusDot:"on"|"off"|bool}
|
||||||
|
//
|
||||||
|
// menuName is the slug of a CMS-managed menu; the actual <ul> items are
|
||||||
|
// resolved by the host menu lookup. This block emits the chrome wrapper plus a
|
||||||
|
// data-menu-name hook the host renderer reads at runtime.
|
||||||
|
func NavbarTerminalBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
data := NavbarTerminalData{
|
||||||
|
MenuName: getStringWithDefault(content, "menuName", "main"),
|
||||||
|
CommandPrefix: getStringWithDefault(content, "commandPrefix", "~/"),
|
||||||
|
StatusDot: getBool(content, "statusDot", true),
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = navbarTerminalComponent(data).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
50
navbar_terminal.templ
Normal file
50
navbar_terminal.templ
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// navbarTerminalComponent renders the cyberpunk:navbar_terminal block.
|
||||||
|
// At ≤768px the link list collapses behind an aria-expanded toggle button.
|
||||||
|
templ navbarTerminalComponent(data NavbarTerminalData) {
|
||||||
|
<nav
|
||||||
|
data-block="cyberpunk:navbar_terminal"
|
||||||
|
class="cyberpunk-navbar w-full"
|
||||||
|
style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));"
|
||||||
|
>
|
||||||
|
<div class="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between gap-4">
|
||||||
|
<div class="flex items-center gap-3 min-w-0">
|
||||||
|
if data.StatusDot {
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="cyberpunk-status-dot inline-block w-2 h-2 rounded-full"
|
||||||
|
style="background-color: hsl(var(--accent)); box-shadow: 0 0 8px hsl(var(--accent));"
|
||||||
|
></span>
|
||||||
|
}
|
||||||
|
<span class="cyberpunk-mono text-sm" style="color: hsl(var(--accent));">
|
||||||
|
{ data.CommandPrefix }
|
||||||
|
</span>
|
||||||
|
<span class="cyberpunk-mono text-sm truncate" style="color: hsl(var(--foreground));">
|
||||||
|
{ data.MenuName }
|
||||||
|
</span>
|
||||||
|
<span aria-hidden="true" class="caret-blink cyberpunk-mono text-sm" style="color: hsl(var(--primary));">_</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="cyberpunk-navbar-links"
|
||||||
|
class="cyberpunk-navbar-toggle md:hidden inline-flex items-center justify-center px-3 py-2 rounded-md cyberpunk-mono uppercase text-xs tracking-widest"
|
||||||
|
style="border: 1px solid hsl(var(--border)); color: hsl(var(--foreground));"
|
||||||
|
data-cyberpunk-navbar-toggle
|
||||||
|
>
|
||||||
|
MENU
|
||||||
|
</button>
|
||||||
|
<ul
|
||||||
|
id="cyberpunk-navbar-links"
|
||||||
|
class="cyberpunk-navbar-links hidden md:flex items-center gap-6 cyberpunk-mono text-sm uppercase tracking-widest"
|
||||||
|
style="color: hsl(var(--muted-foreground));"
|
||||||
|
data-menu-name={ data.MenuName }
|
||||||
|
>
|
||||||
|
<li class="cyberpunk-navbar-hint" style="color: hsl(var(--muted-foreground));">
|
||||||
|
{ "// menu items resolve at runtime" }
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
104
navbar_terminal_templ.go
Normal file
104
navbar_terminal_templ.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// navbarTerminalComponent renders the cyberpunk:navbar_terminal block.
|
||||||
|
// At ≤768px the link list collapses behind an aria-expanded toggle button.
|
||||||
|
func navbarTerminalComponent(data NavbarTerminalData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<nav data-block=\"cyberpunk:navbar_terminal\" class=\"cyberpunk-navbar w-full\" style=\"background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));\"><div class=\"max-w-6xl mx-auto px-4 py-3 flex items-center justify-between gap-4\"><div class=\"flex items-center gap-3 min-w-0\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if data.StatusDot {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<span aria-hidden=\"true\" class=\"cyberpunk-status-dot inline-block w-2 h-2 rounded-full\" style=\"background-color: hsl(var(--accent)); box-shadow: 0 0 8px hsl(var(--accent));\"></span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<span class=\"cyberpunk-mono text-sm\" style=\"color: hsl(var(--accent));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.CommandPrefix)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `navbar_terminal.templ`, Line: 21, Col: 25}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</span> <span class=\"cyberpunk-mono text-sm truncate\" style=\"color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.MenuName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `navbar_terminal.templ`, Line: 24, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span> <span aria-hidden=\"true\" class=\"caret-blink cyberpunk-mono text-sm\" style=\"color: hsl(var(--primary));\">_</span></div><button type=\"button\" aria-expanded=\"false\" aria-controls=\"cyberpunk-navbar-links\" class=\"cyberpunk-navbar-toggle md:hidden inline-flex items-center justify-center px-3 py-2 rounded-md cyberpunk-mono uppercase text-xs tracking-widest\" style=\"border: 1px solid hsl(var(--border)); color: hsl(var(--foreground));\" data-cyberpunk-navbar-toggle>MENU</button><ul id=\"cyberpunk-navbar-links\" class=\"cyberpunk-navbar-links hidden md:flex items-center gap-6 cyberpunk-mono text-sm uppercase tracking-widest\" style=\"color: hsl(var(--muted-foreground));\" data-menu-name=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.MenuName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `navbar_terminal.templ`, Line: 42, Col: 34}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><li class=\"cyberpunk-navbar-hint\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("// menu items resolve at runtime")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `navbar_terminal.templ`, Line: 45, Col: 41}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</li></ul></div></nav>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
12
plugin.mod
Normal file
12
plugin.mod
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[plugin]
|
||||||
|
name = "cyberpunk"
|
||||||
|
display_name = "Cyberpunk"
|
||||||
|
scope = "@themes"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Neon-on-black BlockNinja theme with glitch motifs, monospace accents, and magenta/cyan/lime tri-accent palette for SaaS, dev tools, and crypto."
|
||||||
|
kind = "theme"
|
||||||
|
categories = ["templates", "developer"]
|
||||||
|
tags = ["dark", "neon", "glitch", "monospace", "saas", "developer", "tech", "crypto", "fintech"]
|
||||||
|
|
||||||
|
[compatibility]
|
||||||
|
block_core = ">=0.11.0 <0.12.0"
|
||||||
152
presets.json
Normal file
152
presets.json
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "neon-noir",
|
||||||
|
"name": "Neon Noir",
|
||||||
|
"description": "Magenta-led tri-accent, the Cyberpunk default",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "240 12% 97%",
|
||||||
|
"foreground": "260 25% 10%",
|
||||||
|
"card": "240 10% 99%",
|
||||||
|
"cardForeground": "260 25% 10%",
|
||||||
|
"popover": "240 10% 99%",
|
||||||
|
"popoverForeground": "260 25% 10%",
|
||||||
|
"primary": "320 100% 50%",
|
||||||
|
"primaryForeground": "0 0% 100%",
|
||||||
|
"secondary": "260 20% 92%",
|
||||||
|
"secondaryForeground": "260 25% 10%",
|
||||||
|
"muted": "260 15% 95%",
|
||||||
|
"mutedForeground": "260 12% 40%",
|
||||||
|
"accent": "185 100% 45%",
|
||||||
|
"accentForeground": "260 25% 10%",
|
||||||
|
"destructive": "0 90% 55%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "260 20% 88%",
|
||||||
|
"input": "260 20% 88%",
|
||||||
|
"ring": "320 100% 50%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "260 18% 6%",
|
||||||
|
"foreground": "300 15% 95%",
|
||||||
|
"card": "260 22% 9%",
|
||||||
|
"cardForeground": "300 15% 95%",
|
||||||
|
"popover": "260 22% 9%",
|
||||||
|
"popoverForeground": "300 15% 95%",
|
||||||
|
"primary": "320 100% 60%",
|
||||||
|
"primaryForeground": "260 18% 6%",
|
||||||
|
"secondary": "260 25% 14%",
|
||||||
|
"secondaryForeground": "300 15% 95%",
|
||||||
|
"muted": "260 20% 12%",
|
||||||
|
"mutedForeground": "260 15% 65%",
|
||||||
|
"accent": "185 100% 55%",
|
||||||
|
"accentForeground": "260 18% 6%",
|
||||||
|
"destructive": "0 95% 62%",
|
||||||
|
"destructiveForeground": "260 18% 6%",
|
||||||
|
"border": "260 30% 18%",
|
||||||
|
"input": "260 30% 16%",
|
||||||
|
"ring": "320 100% 60%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "acid-rain",
|
||||||
|
"name": "Acid Rain",
|
||||||
|
"description": "Cyan-led, cooler chrome variant",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "200 25% 97%",
|
||||||
|
"foreground": "220 30% 10%",
|
||||||
|
"card": "200 20% 99%",
|
||||||
|
"cardForeground": "220 30% 10%",
|
||||||
|
"popover": "200 20% 99%",
|
||||||
|
"popoverForeground": "220 30% 10%",
|
||||||
|
"primary": "185 100% 40%",
|
||||||
|
"primaryForeground": "220 25% 5%",
|
||||||
|
"secondary": "200 25% 92%",
|
||||||
|
"secondaryForeground": "220 30% 10%",
|
||||||
|
"muted": "200 20% 95%",
|
||||||
|
"mutedForeground": "220 15% 40%",
|
||||||
|
"accent": "320 95% 55%",
|
||||||
|
"accentForeground": "0 0% 100%",
|
||||||
|
"destructive": "0 90% 55%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "200 25% 88%",
|
||||||
|
"input": "200 25% 88%",
|
||||||
|
"ring": "185 100% 40%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "220 25% 5%",
|
||||||
|
"foreground": "180 25% 92%",
|
||||||
|
"card": "220 28% 8%",
|
||||||
|
"cardForeground": "180 25% 92%",
|
||||||
|
"popover": "220 28% 8%",
|
||||||
|
"popoverForeground": "180 25% 92%",
|
||||||
|
"primary": "185 100% 55%",
|
||||||
|
"primaryForeground": "220 25% 5%",
|
||||||
|
"secondary": "220 28% 13%",
|
||||||
|
"secondaryForeground": "180 25% 92%",
|
||||||
|
"muted": "220 25% 11%",
|
||||||
|
"mutedForeground": "200 20% 65%",
|
||||||
|
"accent": "320 100% 65%",
|
||||||
|
"accentForeground": "220 25% 5%",
|
||||||
|
"destructive": "0 95% 62%",
|
||||||
|
"destructiveForeground": "220 25% 5%",
|
||||||
|
"border": "200 40% 18%",
|
||||||
|
"input": "200 40% 16%",
|
||||||
|
"ring": "185 100% 55%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "toxic-bloom",
|
||||||
|
"name": "Toxic Bloom",
|
||||||
|
"description": "Lime-led, we shipped at 3am",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "100 15% 97%",
|
||||||
|
"foreground": "120 20% 10%",
|
||||||
|
"card": "100 12% 99%",
|
||||||
|
"cardForeground": "120 20% 10%",
|
||||||
|
"popover": "100 12% 99%",
|
||||||
|
"popoverForeground": "120 20% 10%",
|
||||||
|
"primary": "90 90% 38%",
|
||||||
|
"primaryForeground": "120 25% 5%",
|
||||||
|
"secondary": "100 15% 92%",
|
||||||
|
"secondaryForeground": "120 20% 10%",
|
||||||
|
"muted": "100 12% 95%",
|
||||||
|
"mutedForeground": "120 12% 40%",
|
||||||
|
"accent": "320 100% 55%",
|
||||||
|
"accentForeground": "0 0% 100%",
|
||||||
|
"destructive": "0 90% 55%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "100 15% 88%",
|
||||||
|
"input": "100 15% 88%",
|
||||||
|
"ring": "90 90% 38%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "120 15% 5%",
|
||||||
|
"foreground": "90 30% 92%",
|
||||||
|
"card": "120 18% 8%",
|
||||||
|
"cardForeground": "90 30% 92%",
|
||||||
|
"popover": "120 18% 8%",
|
||||||
|
"popoverForeground": "90 30% 92%",
|
||||||
|
"primary": "90 100% 55%",
|
||||||
|
"primaryForeground": "120 25% 5%",
|
||||||
|
"secondary": "120 18% 13%",
|
||||||
|
"secondaryForeground": "90 30% 92%",
|
||||||
|
"muted": "120 15% 11%",
|
||||||
|
"mutedForeground": "100 15% 65%",
|
||||||
|
"accent": "320 100% 65%",
|
||||||
|
"accentForeground": "120 25% 5%",
|
||||||
|
"destructive": "0 95% 62%",
|
||||||
|
"destructiveForeground": "120 25% 5%",
|
||||||
|
"border": "100 25% 18%",
|
||||||
|
"input": "100 25% 16%",
|
||||||
|
"ring": "90 100% 55%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
190
register.go
Normal file
190
register.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/a-h/templ"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/core/plugin"
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// wrap adapts a templ-returning render function to templates.TemplateFunc.
|
||||||
|
// templ.Component already implements templates.HTMLComponent via Render.
|
||||||
|
func wrap(f func(ctx context.Context, doc map[string]any) templ.Component) templates.TemplateFunc {
|
||||||
|
return func(ctx context.Context, doc map[string]any) templates.HTMLComponent {
|
||||||
|
return f(ctx, doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register is the plugin entry point that registers the Cyberpunk system template,
|
||||||
|
// page templates, theme-owned blocks, built-in overrides, and the email wrapper.
|
||||||
|
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
||||||
|
// 1. System template metadata.
|
||||||
|
tr.RegisterSystemTemplate(templates.SystemTemplateMeta{
|
||||||
|
Key: "cyberpunk",
|
||||||
|
Title: "Cyberpunk",
|
||||||
|
Description: "Neon-on-black BlockNinja theme with glitch motifs, monospace accents, and magenta/cyan/lime tri-accent palette for SaaS, dev tools, and crypto.",
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2. Page templates — slot lists match docs/works/cyberpunk.md byte-for-byte.
|
||||||
|
if err := tr.RegisterPageTemplate("cyberpunk", templates.PageTemplateMeta{
|
||||||
|
Key: "default",
|
||||||
|
Title: "Default",
|
||||||
|
Description: "Standard dark page with scanline overlay, sticky terminal-style header",
|
||||||
|
Slots: []string{"header", "main", "footer"},
|
||||||
|
}, wrap(RenderCyberpunk)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("cyberpunk", templates.PageTemplateMeta{
|
||||||
|
Key: "landing",
|
||||||
|
Title: "Landing",
|
||||||
|
Description: "Hero with glitch H1, neon CTA, feature grid, social proof, footer",
|
||||||
|
Slots: []string{"hero", "features", "cta", "footer"},
|
||||||
|
}, wrap(RenderCyberpunkLanding)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("cyberpunk", templates.PageTemplateMeta{
|
||||||
|
Key: "article",
|
||||||
|
Title: "Article",
|
||||||
|
Description: "Mono-metadata header, prose column, code-friendly aside",
|
||||||
|
Slots: []string{"header", "main", "aside", "footer"},
|
||||||
|
}, wrap(RenderCyberpunkArticle)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tr.RegisterPageTemplate("cyberpunk", templates.PageTemplateMeta{
|
||||||
|
Key: "full-width",
|
||||||
|
Title: "Full Width",
|
||||||
|
Description: "Edge-to-edge dashboard / showcase layout, no max-width",
|
||||||
|
Slots: []string{"header", "main", "footer"},
|
||||||
|
}, wrap(RenderCyberpunkFullWidth)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Load block schemas BEFORE registering any blocks.
|
||||||
|
if err := br.LoadSchemasFromFS(Schemas()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Theme-owned blocks (seven, per spec section "Blocks to build").
|
||||||
|
br.Register(HeroGlitchMeta, HeroGlitchBlock)
|
||||||
|
br.Register(CTATerminalMeta, CTATerminalBlock)
|
||||||
|
br.Register(NavbarTerminalMeta, NavbarTerminalBlock)
|
||||||
|
br.Register(FooterGridMeta, FooterGridBlock)
|
||||||
|
br.Register(FeatureCardNeonMeta, FeatureCardNeonBlock)
|
||||||
|
br.Register(StatsGlowMeta, StatsGlowBlock)
|
||||||
|
br.Register(CodeNeonMeta, CodeNeonBlock)
|
||||||
|
|
||||||
|
// 5. Built-in block overrides — applied only when this theme is active.
|
||||||
|
br.RegisterTemplateOverride("cyberpunk", "heading", CyberpunkHeadingBlock)
|
||||||
|
br.RegisterTemplateOverride("cyberpunk", "text", CyberpunkTextBlock)
|
||||||
|
br.RegisterTemplateOverride("cyberpunk", "button", CyberpunkButtonBlock)
|
||||||
|
br.RegisterTemplateOverride("cyberpunk", "card", CyberpunkCardBlock)
|
||||||
|
|
||||||
|
// 6. Email wrapper.
|
||||||
|
tr.RegisterEmailWrapper("cyberpunk", CyberpunkEmailWrapper)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMasterPages returns the three master pages Cyberpunk seeds on first load.
|
||||||
|
// Spec table — section "Master pages" — drives keys, blocks, slots and sort orders.
|
||||||
|
func DefaultMasterPages() []plugin.MasterPageDefinition {
|
||||||
|
return []plugin.MasterPageDefinition{
|
||||||
|
{
|
||||||
|
Key: "cyberpunk:default-master",
|
||||||
|
Title: "Cyberpunk Default Master",
|
||||||
|
PageTemplates: []string{"default", "article"},
|
||||||
|
Blocks: []plugin.MasterPageBlock{
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:navbar_terminal",
|
||||||
|
Title: "Top Nav",
|
||||||
|
Content: map[string]any{"menuName": "main", "commandPrefix": "~/"},
|
||||||
|
Slot: "header",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "slot",
|
||||||
|
Title: "Main Slot",
|
||||||
|
Content: map[string]any{"slotName": "main", "placeholder": "// content"},
|
||||||
|
Slot: "main",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:footer_grid",
|
||||||
|
Title: "Site Footer",
|
||||||
|
Content: map[string]any{"showStatus": true, "buildHash": "auto"},
|
||||||
|
Slot: "footer",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "cyberpunk:landing-master",
|
||||||
|
Title: "Cyberpunk Landing Master",
|
||||||
|
PageTemplates: []string{"landing"},
|
||||||
|
Blocks: []plugin.MasterPageBlock{
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:hero_glitch",
|
||||||
|
Title: "Glitch Hero",
|
||||||
|
Content: map[string]any{"eyebrow": "NEW", "headline": "Ship like it's 2049", "cta": map[string]any{"label": "Get the SDK", "href": "#"}},
|
||||||
|
Slot: "hero",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "slot",
|
||||||
|
Title: "Features Slot",
|
||||||
|
Content: map[string]any{"slotName": "features"},
|
||||||
|
Slot: "features",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:cta_terminal",
|
||||||
|
Title: "CTA Strip",
|
||||||
|
Content: map[string]any{"prompt": "$ npm i your-thing", "button": "Copy"},
|
||||||
|
Slot: "cta",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:footer_grid",
|
||||||
|
Title: "Site Footer",
|
||||||
|
Content: map[string]any{"showStatus": true},
|
||||||
|
Slot: "footer",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "cyberpunk:full-master",
|
||||||
|
Title: "Cyberpunk Full Master",
|
||||||
|
PageTemplates: []string{"full-width"},
|
||||||
|
Blocks: []plugin.MasterPageBlock{
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:navbar_terminal",
|
||||||
|
Title: "Top Nav",
|
||||||
|
Content: map[string]any{"menuName": "main"},
|
||||||
|
Slot: "header",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "slot",
|
||||||
|
Title: "Main Slot",
|
||||||
|
Content: map[string]any{"slotName": "main"},
|
||||||
|
Slot: "main",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlockKey: "cyberpunk:footer_grid",
|
||||||
|
Title: "Site Footer",
|
||||||
|
Content: map[string]any{"showStatus": false},
|
||||||
|
Slot: "footer",
|
||||||
|
SortOrder: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
25
registration.go
Normal file
25
registration.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/core/plugin"
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Registration is the compile-time plugin registration for the Cyberpunk theme.
|
||||||
|
var Registration = plugin.PluginRegistration{
|
||||||
|
Name: "cyberpunk",
|
||||||
|
Version: plugin.ParseModVersion(pluginModBytes),
|
||||||
|
Register: func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
|
||||||
|
return Register(tr, br)
|
||||||
|
},
|
||||||
|
Assets: func() http.Handler { return AssetsHandler() },
|
||||||
|
Schemas: func() fs.FS { return Schemas() },
|
||||||
|
ThemePresets: func() []byte { return ThemePresets() },
|
||||||
|
BundledFonts: func() []byte { return BundledFonts() },
|
||||||
|
MasterPages: func() []plugin.MasterPageDefinition { return DefaultMasterPages() },
|
||||||
|
CSSManifest: func() *plugin.CSSManifest { return ThemeCSSManifest() },
|
||||||
|
}
|
||||||
30
schemas/code_neon.schema.json
Normal file
30
schemas/code_neon.schema.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Code Block",
|
||||||
|
"description": "Server-rendered code block with language label and optional copy button.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"language": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Language",
|
||||||
|
"description": "Source language used as the chroma-style class and figcaption label",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["text", "bash", "shell", "javascript", "typescript", "go", "json", "html", "css", "yaml", "sql", "rust", "python"],
|
||||||
|
"default": "text"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Code",
|
||||||
|
"description": "Block contents",
|
||||||
|
"x-editor": "textarea"
|
||||||
|
},
|
||||||
|
"copy": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Copy button",
|
||||||
|
"description": "Show a copy-to-clipboard button in the caption",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["on", "off"],
|
||||||
|
"default": "on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
schemas/cta_terminal.schema.json
Normal file
34
schemas/cta_terminal.schema.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Terminal CTA",
|
||||||
|
"description": "Faux $ prompt strip with blinking caret and optional copy-to-clipboard.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"prompt": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Prompt",
|
||||||
|
"description": "The command text following the $ glyph (e.g. npm i your-thing)",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Button label",
|
||||||
|
"description": "Label for the action button (e.g. Copy)",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"href": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Button URL",
|
||||||
|
"description": "Optional URL the button points at when copy is off",
|
||||||
|
"x-editor": "link"
|
||||||
|
},
|
||||||
|
"copyable": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Copy to clipboard",
|
||||||
|
"description": "When 'on' the button copies the prompt text instead of navigating",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["on", "off"],
|
||||||
|
"default": "on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
schemas/feature_card_neon.schema.json
Normal file
34
schemas/feature_card_neon.schema.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Neon Feature Card",
|
||||||
|
"description": "Single feature card with icon, title, body and per-card neon edge glow.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"icon": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Icon",
|
||||||
|
"description": "Media reference for the feature icon",
|
||||||
|
"x-editor": "media"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Title",
|
||||||
|
"description": "Card heading",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Body",
|
||||||
|
"description": "Supporting rich text",
|
||||||
|
"x-editor": "richtext"
|
||||||
|
},
|
||||||
|
"accent": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Accent",
|
||||||
|
"description": "Neon edge colour: magenta, cyan or lime",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["magenta", "cyan", "lime"],
|
||||||
|
"default": "magenta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
schemas/footer_grid.schema.json
Normal file
52
schemas/footer_grid.schema.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Grid Footer",
|
||||||
|
"description": "Multi-column footer with mono column titles, optional status pill, and a build-hash readout.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"showStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Show status pill",
|
||||||
|
"description": "Display the OK/uptime status pill at the bottom right",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["on", "off"],
|
||||||
|
"default": "on"
|
||||||
|
},
|
||||||
|
"buildHash": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Build hash",
|
||||||
|
"description": "Short build hash or version readout (e.g. 'a1b2c3' or 'auto')",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Columns",
|
||||||
|
"description": "Footer columns with links",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "collection",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Column title",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Links",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "collection",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": { "type": "string", "title": "Label", "x-editor": "text" },
|
||||||
|
"href": { "type": "string", "title": "Href", "x-editor": "text" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
schemas/hero_glitch.schema.json
Normal file
54
schemas/hero_glitch.schema.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Glitch Hero",
|
||||||
|
"description": "Cyberpunk hero section with RGB-split H1, eyebrow, sub, primary + secondary CTAs and an optional scanline overlay.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"eyebrow": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Eyebrow",
|
||||||
|
"description": "Short mono caps label above the headline (e.g. NEW)",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"headline": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Headline",
|
||||||
|
"description": "Main H1; rendered with RGB-split glitch text-shadow",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"sub": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Sub",
|
||||||
|
"description": "Supporting paragraph beneath the headline",
|
||||||
|
"x-editor": "textarea"
|
||||||
|
},
|
||||||
|
"cta": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Primary CTA",
|
||||||
|
"description": "Primary call to action {label, href}",
|
||||||
|
"x-editor": "link",
|
||||||
|
"properties": {
|
||||||
|
"label": { "type": "string", "title": "Label", "x-editor": "text" },
|
||||||
|
"href": { "type": "string", "title": "Href", "x-editor": "text" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"secondaryCta": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Secondary CTA",
|
||||||
|
"description": "Optional secondary call to action {label, href}",
|
||||||
|
"x-editor": "link",
|
||||||
|
"properties": {
|
||||||
|
"label": { "type": "string", "title": "Label", "x-editor": "text" },
|
||||||
|
"href": { "type": "string", "title": "Href", "x-editor": "text" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scanlines": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Scanlines",
|
||||||
|
"description": "Toggle the 4% scanline overlay inside the hero section",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["on", "off"],
|
||||||
|
"default": "on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
schemas/navbar_terminal.schema.json
Normal file
29
schemas/navbar_terminal.schema.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Terminal Navbar",
|
||||||
|
"description": "Replaces the built-in navbar with a mono-spaced $ prompt navbar, optional status dot, and a tap-friendly mobile menu toggle.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"menuName": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Menu",
|
||||||
|
"description": "Slug of the CMS menu to render (e.g. 'main')",
|
||||||
|
"x-editor": "menu-select"
|
||||||
|
},
|
||||||
|
"commandPrefix": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Command prefix",
|
||||||
|
"description": "Leading prompt characters (default '~/')",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": "~/"
|
||||||
|
},
|
||||||
|
"statusDot": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Status dot",
|
||||||
|
"description": "Show a glowing status indicator (uptime hint)",
|
||||||
|
"x-editor": "select",
|
||||||
|
"enum": ["on", "off"],
|
||||||
|
"default": "on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
schemas/stats_glow.schema.json
Normal file
38
schemas/stats_glow.schema.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Glow Stats",
|
||||||
|
"description": "Big mono numerals with accent underline and short labels.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Items",
|
||||||
|
"description": "Stats to display",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "collection",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Value",
|
||||||
|
"description": "Big numeric or string value (e.g. 99.99)",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Label",
|
||||||
|
"description": "Short uppercase caption beneath the value",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"suffix": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Suffix",
|
||||||
|
"description": "Trailing unit or symbol (e.g. % or ms), accent-coloured",
|
||||||
|
"x-editor": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
stats_glow.go
Normal file
43
stats_glow.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/blocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatsGlowMeta defines metadata for the cyberpunk:stats_glow block.
|
||||||
|
var StatsGlowMeta = blocks.BlockMeta{
|
||||||
|
Key: "stats_glow",
|
||||||
|
Title: "Glow Stats",
|
||||||
|
Description: "Big mono numerals with neon accent underline",
|
||||||
|
Category: "display",
|
||||||
|
Source: "cyberpunk",
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatGlowItem is one stat in the grid.
|
||||||
|
type StatGlowItem struct {
|
||||||
|
Value string
|
||||||
|
Label string
|
||||||
|
Suffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsGlowBlock renders the stats_glow block.
|
||||||
|
//
|
||||||
|
// Content shape:
|
||||||
|
// {items:[{value, label, suffix}]}
|
||||||
|
func StatsGlowBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
raw := getSlice(content, "items")
|
||||||
|
items := make([]StatGlowItem, 0, len(raw))
|
||||||
|
for _, item := range raw {
|
||||||
|
items = append(items, StatGlowItem{
|
||||||
|
Value: getString(item, "value"),
|
||||||
|
Label: getString(item, "label"),
|
||||||
|
Suffix: getString(item, "suffix"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = statsGlowComponent(items).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
52
stats_glow.templ
Normal file
52
stats_glow.templ
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// statsGridColumns returns the tailwind grid-column class for n stats.
|
||||||
|
func statsGridColumns(n int) string {
|
||||||
|
switch n {
|
||||||
|
case 0, 1:
|
||||||
|
return "grid-cols-1"
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 3:
|
||||||
|
return "grid-cols-1 sm:grid-cols-3"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// statsGlowComponent renders the cyberpunk:stats_glow block.
|
||||||
|
templ statsGlowComponent(items []StatGlowItem) {
|
||||||
|
<section
|
||||||
|
data-block="cyberpunk:stats_glow"
|
||||||
|
class="cyberpunk-stats w-full py-16"
|
||||||
|
style="background-color: hsl(var(--background));"
|
||||||
|
>
|
||||||
|
<div class="max-w-6xl mx-auto px-4">
|
||||||
|
if len(items) == 0 {
|
||||||
|
<div class="text-center cyberpunk-mono text-xs uppercase tracking-widest" style="color: hsl(var(--muted-foreground));">
|
||||||
|
{ "// add items to render stats" }
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<div class={ "grid gap-8 text-center", statsGridColumns(len(items)) }>
|
||||||
|
for _, stat := range items {
|
||||||
|
<div class="cyberpunk-stat-cell">
|
||||||
|
<div
|
||||||
|
class="cyberpunk-mono cyberpunk-stat-number text-5xl md:text-6xl font-bold tracking-tight"
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace);"
|
||||||
|
>
|
||||||
|
<span>{ stat.Value }</span>
|
||||||
|
if stat.Suffix != "" {
|
||||||
|
<span style="color: hsl(var(--primary));">{ stat.Suffix }</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="cyberpunk-stat-underline mx-auto mt-2 mb-3 h-px w-12" style="background-color: hsl(var(--accent));"></div>
|
||||||
|
<div class="cyberpunk-mono cyberpunk-stat-label text-xs uppercase tracking-widest" style="color: hsl(var(--muted-foreground));">
|
||||||
|
{ stat.Label }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
160
stats_glow_templ.go
Normal file
160
stats_glow_templ.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// statsGridColumns returns the tailwind grid-column class for n stats.
|
||||||
|
func statsGridColumns(n int) string {
|
||||||
|
switch n {
|
||||||
|
case 0, 1:
|
||||||
|
return "grid-cols-1"
|
||||||
|
case 2:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2"
|
||||||
|
case 3:
|
||||||
|
return "grid-cols-1 sm:grid-cols-3"
|
||||||
|
default:
|
||||||
|
return "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// statsGlowComponent renders the cyberpunk:stats_glow block.
|
||||||
|
func statsGlowComponent(items []StatGlowItem) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<section data-block=\"cyberpunk:stats_glow\" class=\"cyberpunk-stats w-full py-16\" style=\"background-color: hsl(var(--background));\"><div class=\"max-w-6xl mx-auto px-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if len(items) == 0 {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"text-center cyberpunk-mono text-xs uppercase tracking-widest\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("// add items to render stats")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats_glow.templ`, Line: 27, Col: 37}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var templ_7745c5c3_Var3 = []any{"grid gap-8 text-center", statsGridColumns(len(items))}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats_glow.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, stat := range items {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"cyberpunk-stat-cell\"><div class=\"cyberpunk-mono cyberpunk-stat-number text-5xl md:text-6xl font-bold tracking-tight\" style=\"color: hsl(var(--foreground)); font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace);\"><span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(stat.Value)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats_glow.templ`, Line: 37, Col: 26}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</span> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if stat.Suffix != "" {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<span style=\"color: hsl(var(--primary));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(stat.Suffix)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats_glow.templ`, Line: 39, Col: 64}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div><div class=\"cyberpunk-stat-underline mx-auto mt-2 mb-3 h-px w-12\" style=\"background-color: hsl(var(--accent));\"></div><div class=\"cyberpunk-mono cyberpunk-stat-label text-xs uppercase tracking-widest\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(stat.Label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats_glow.templ`, Line: 44, Col: 20}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
237
template.templ
Normal file
237
template.templ
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates/bn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageData carries the rendered slots and assorted page chrome data passed
|
||||||
|
// from the CMS into a Cyberpunk page template.
|
||||||
|
type PageData struct {
|
||||||
|
Title string
|
||||||
|
Slots map[string]string
|
||||||
|
ThemeMode string
|
||||||
|
ThemeCSS string
|
||||||
|
SiteSettings bn.SiteSettingsData
|
||||||
|
PageMeta bn.PageMeta
|
||||||
|
StructuredData string
|
||||||
|
CSSHash string
|
||||||
|
PageviewNonce string
|
||||||
|
EngagementConfig bn.EngagementConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCyberpunkPageData converts the loose doc map the renderer is handed into a typed PageData.
|
||||||
|
func parseCyberpunkPageData(doc map[string]any) PageData {
|
||||||
|
title := "Untitled"
|
||||||
|
if t, ok := doc["title"].(string); ok && t != "" {
|
||||||
|
title = t
|
||||||
|
}
|
||||||
|
|
||||||
|
slots := map[string]string{}
|
||||||
|
if s, ok := doc["slots"].(map[string]string); ok {
|
||||||
|
slots = s
|
||||||
|
}
|
||||||
|
|
||||||
|
themeCSS, _ := doc["theme_css"].(string)
|
||||||
|
structuredData, _ := doc["structured_data"].(string)
|
||||||
|
cssHash, _ := doc["css_hash"].(string)
|
||||||
|
pageviewNonce, _ := doc["pageview_nonce"].(string)
|
||||||
|
|
||||||
|
themeMode := "dark"
|
||||||
|
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
||||||
|
themeMode = tm
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageData{
|
||||||
|
Title: title,
|
||||||
|
Slots: slots,
|
||||||
|
ThemeMode: themeMode,
|
||||||
|
ThemeCSS: themeCSS,
|
||||||
|
SiteSettings: bn.ParseSiteSettings(doc),
|
||||||
|
PageMeta: bn.ParsePageMeta(doc),
|
||||||
|
StructuredData: structuredData,
|
||||||
|
CSSHash: cssHash,
|
||||||
|
PageviewNonce: pageviewNonce,
|
||||||
|
EngagementConfig: bn.ParseEngagementConfig(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanlineOverlay renders the global scanline overlay element (fixed, pointer-events: none).
|
||||||
|
templ scanlineOverlay() {
|
||||||
|
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-cyberpunk-scanlines></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cyberpunk renders the default page template.
|
||||||
|
templ Cyberpunk(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="cyberpunk-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground));">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
@scanlineOverlay()
|
||||||
|
<header class="cyberpunk-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full max-w-6xl mx-auto px-4 py-12">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
@templ.Raw(main)
|
||||||
|
} else {
|
||||||
|
<div class="py-20 text-center cyberpunk-empty" style="color: hsl(var(--muted-foreground));">
|
||||||
|
<p class="cyberpunk-mono">No content blocks assigned to this page.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkLanding renders the marketing landing template with hero/features/cta/footer slots.
|
||||||
|
templ CyberpunkLanding(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="cyberpunk-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground));">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
@scanlineOverlay()
|
||||||
|
<section class="w-full">
|
||||||
|
@templ.Raw(data.Slots["hero"])
|
||||||
|
</section>
|
||||||
|
<section class="w-full max-w-6xl mx-auto px-4 py-16">
|
||||||
|
@templ.Raw(data.Slots["features"])
|
||||||
|
</section>
|
||||||
|
<section class="w-full">
|
||||||
|
@templ.Raw(data.Slots["cta"])
|
||||||
|
</section>
|
||||||
|
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkArticle renders the article template with mono-metadata header and a code-friendly aside.
|
||||||
|
templ CyberpunkArticle(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="cyberpunk-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground));">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
@scanlineOverlay()
|
||||||
|
<header class="w-full" style="border-bottom: 1px solid hsl(var(--border));">
|
||||||
|
<div class="max-w-4xl mx-auto px-4 py-6 cyberpunk-mono text-xs uppercase tracking-widest">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full max-w-4xl mx-auto px-4 py-12 grid gap-10 lg:grid-cols-[1fr_240px]">
|
||||||
|
<article class="cyberpunk-prose prose prose-invert max-w-none">
|
||||||
|
@templ.Raw(data.Slots["main"])
|
||||||
|
</article>
|
||||||
|
<aside class="cyberpunk-aside cyberpunk-mono text-sm" style="color: hsl(var(--muted-foreground));">
|
||||||
|
@templ.Raw(data.Slots["aside"])
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkFullWidth renders the edge-to-edge template (no max-width clamping on main).
|
||||||
|
templ CyberpunkFullWidth(data PageData) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
@bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
})
|
||||||
|
<body class="cyberpunk-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground));">
|
||||||
|
@bn.AdminBypassBanner(data.SiteSettings)
|
||||||
|
@scanlineOverlay()
|
||||||
|
<header class="cyberpunk-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["header"])
|
||||||
|
</header>
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
@templ.Raw(main)
|
||||||
|
} else {
|
||||||
|
<div class="py-20 text-center cyberpunk-mono" style="color: hsl(var(--muted-foreground));">
|
||||||
|
<p>No content blocks assigned to this page.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">
|
||||||
|
@templ.Raw(data.Slots["footer"])
|
||||||
|
</footer>
|
||||||
|
@bn.BodyEnd(data.SiteSettings)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunk renders the default Cyberpunk page template.
|
||||||
|
func RenderCyberpunk(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return Cyberpunk(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkLanding renders the Cyberpunk landing page template.
|
||||||
|
func RenderCyberpunkLanding(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkLanding(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkArticle renders the Cyberpunk article page template.
|
||||||
|
func RenderCyberpunkArticle(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkArticle(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkFullWidth renders the Cyberpunk full-width page template.
|
||||||
|
func RenderCyberpunkFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkFullWidth(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
520
template_templ.go
Normal file
520
template_templ.go
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/core/templates/bn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageData carries the rendered slots and assorted page chrome data passed
|
||||||
|
// from the CMS into a Cyberpunk page template.
|
||||||
|
type PageData struct {
|
||||||
|
Title string
|
||||||
|
Slots map[string]string
|
||||||
|
ThemeMode string
|
||||||
|
ThemeCSS string
|
||||||
|
SiteSettings bn.SiteSettingsData
|
||||||
|
PageMeta bn.PageMeta
|
||||||
|
StructuredData string
|
||||||
|
CSSHash string
|
||||||
|
PageviewNonce string
|
||||||
|
EngagementConfig bn.EngagementConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCyberpunkPageData converts the loose doc map the renderer is handed into a typed PageData.
|
||||||
|
func parseCyberpunkPageData(doc map[string]any) PageData {
|
||||||
|
title := "Untitled"
|
||||||
|
if t, ok := doc["title"].(string); ok && t != "" {
|
||||||
|
title = t
|
||||||
|
}
|
||||||
|
|
||||||
|
slots := map[string]string{}
|
||||||
|
if s, ok := doc["slots"].(map[string]string); ok {
|
||||||
|
slots = s
|
||||||
|
}
|
||||||
|
|
||||||
|
themeCSS, _ := doc["theme_css"].(string)
|
||||||
|
structuredData, _ := doc["structured_data"].(string)
|
||||||
|
cssHash, _ := doc["css_hash"].(string)
|
||||||
|
pageviewNonce, _ := doc["pageview_nonce"].(string)
|
||||||
|
|
||||||
|
themeMode := "dark"
|
||||||
|
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
|
||||||
|
themeMode = tm
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageData{
|
||||||
|
Title: title,
|
||||||
|
Slots: slots,
|
||||||
|
ThemeMode: themeMode,
|
||||||
|
ThemeCSS: themeCSS,
|
||||||
|
SiteSettings: bn.ParseSiteSettings(doc),
|
||||||
|
PageMeta: bn.ParsePageMeta(doc),
|
||||||
|
StructuredData: structuredData,
|
||||||
|
CSSHash: cssHash,
|
||||||
|
PageviewNonce: pageviewNonce,
|
||||||
|
EngagementConfig: bn.ParseEngagementConfig(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanlineOverlay renders the global scanline overlay element (fixed, pointer-events: none).
|
||||||
|
func scanlineOverlay() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div aria-hidden=\"true\" class=\"scanlines pointer-events-none fixed inset-0 z-50\" data-cyberpunk-scanlines></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cyberpunk renders the default page template.
|
||||||
|
func Cyberpunk(data PageData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var2 == nil {
|
||||||
|
templ_7745c5c3_Var2 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!doctype html><html lang=\"en\" class=\"dark\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<body class=\"cyberpunk-body min-h-screen flex flex-col antialiased\" style=\"background-color: hsl(var(--background)); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = scanlineOverlay().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<header class=\"cyberpunk-chrome w-full sticky top-0 z-40\" style=\"background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</header><main class=\"flex-grow w-full max-w-6xl mx-auto px-4 py-12\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"py-20 text-center cyberpunk-empty\" style=\"color: hsl(var(--muted-foreground));\"><p class=\"cyberpunk-mono\">No content blocks assigned to this page.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</main><footer class=\"w-full mt-auto\" style=\"border-top: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</footer>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkLanding renders the marketing landing template with hero/features/cta/footer slots.
|
||||||
|
func CyberpunkLanding(data PageData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var3 == nil {
|
||||||
|
templ_7745c5c3_Var3 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<!doctype html><html lang=\"en\" class=\"dark\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<body class=\"cyberpunk-body min-h-screen flex flex-col antialiased\" style=\"background-color: hsl(var(--background)); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = scanlineOverlay().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<section class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["hero"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</section><section class=\"w-full max-w-6xl mx-auto px-4 py-16\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["features"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</section><section class=\"w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["cta"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</section><footer class=\"w-full mt-auto\" style=\"border-top: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</footer>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkArticle renders the article template with mono-metadata header and a code-friendly aside.
|
||||||
|
func CyberpunkArticle(data PageData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var4 == nil {
|
||||||
|
templ_7745c5c3_Var4 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<!doctype html><html lang=\"en\" class=\"dark\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<body class=\"cyberpunk-body min-h-screen flex flex-col antialiased\" style=\"background-color: hsl(var(--background)); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = scanlineOverlay().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<header class=\"w-full\" style=\"border-bottom: 1px solid hsl(var(--border));\"><div class=\"max-w-4xl mx-auto px-4 py-6 cyberpunk-mono text-xs uppercase tracking-widest\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div></header><main class=\"flex-grow w-full max-w-4xl mx-auto px-4 py-12 grid gap-10 lg:grid-cols-[1fr_240px]\"><article class=\"cyberpunk-prose prose prose-invert max-w-none\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["main"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</article><aside class=\"cyberpunk-aside cyberpunk-mono text-sm\" style=\"color: hsl(var(--muted-foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["aside"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</aside></main><footer class=\"w-full mt-auto\" style=\"border-top: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</footer>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyberpunkFullWidth renders the edge-to-edge template (no max-width clamping on main).
|
||||||
|
func CyberpunkFullWidth(data PageData) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var5 == nil {
|
||||||
|
templ_7745c5c3_Var5 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<!doctype html><html lang=\"en\" class=\"dark\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.Head(bn.HeadData{
|
||||||
|
Title: data.Title,
|
||||||
|
Settings: data.SiteSettings,
|
||||||
|
PageMeta: data.PageMeta,
|
||||||
|
ThemeMode: data.ThemeMode,
|
||||||
|
ThemeCSS: data.ThemeCSS,
|
||||||
|
PluginStyles: []string{"/templates/cyberpunk/css/cyberpunk.css"},
|
||||||
|
StructuredData: data.StructuredData,
|
||||||
|
CSSHash: data.CSSHash,
|
||||||
|
PageviewNonce: data.PageviewNonce,
|
||||||
|
EngagementConfig: data.EngagementConfig,
|
||||||
|
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<body class=\"cyberpunk-body min-h-screen flex flex-col antialiased\" style=\"background-color: hsl(var(--background)); color: hsl(var(--foreground));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.AdminBypassBanner(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = scanlineOverlay().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<header class=\"cyberpunk-chrome w-full sticky top-0 z-40\" style=\"background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["header"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</header><main class=\"flex-grow w-full\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if main, ok := data.Slots["main"]; ok && main != "" {
|
||||||
|
templ_7745c5c3_Err = templ.Raw(main).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"py-20 text-center cyberpunk-mono\" style=\"color: hsl(var(--muted-foreground));\"><p>No content blocks assigned to this page.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</main><footer class=\"w-full mt-auto\" style=\"border-top: 1px solid hsl(var(--border));\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(data.Slots["footer"]).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</footer>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = bn.BodyEnd(data.SiteSettings).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunk renders the default Cyberpunk page template.
|
||||||
|
func RenderCyberpunk(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return Cyberpunk(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkLanding renders the Cyberpunk landing page template.
|
||||||
|
func RenderCyberpunkLanding(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkLanding(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkArticle renders the Cyberpunk article page template.
|
||||||
|
func RenderCyberpunkArticle(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkArticle(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderCyberpunkFullWidth renders the Cyberpunk full-width page template.
|
||||||
|
func RenderCyberpunkFullWidth(ctx context.Context, doc map[string]any) templ.Component {
|
||||||
|
return CyberpunkFullWidth(parseCyberpunkPageData(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
16
text_override.go
Normal file
16
text_override.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CyberpunkTextBlock renders the built-in text block with cyberpunk styling:
|
||||||
|
// scanline-friendly leading and accent-coloured <code> inline spans.
|
||||||
|
func CyberpunkTextBlock(ctx context.Context, content map[string]any) string {
|
||||||
|
text := getString(content, "text")
|
||||||
|
class := getString(content, "class")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_ = cyberpunkTextComponent(text, class).Render(ctx, &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
12
text_override.templ
Normal file
12
text_override.templ
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// cyberpunkTextComponent renders the override for the built-in text block:
|
||||||
|
// shadcn `prose` invert with cyberpunk leading and accent inline code.
|
||||||
|
templ cyberpunkTextComponent(text, class string) {
|
||||||
|
<div
|
||||||
|
class={ "cyberpunk-prose prose prose-invert max-w-none leading-relaxed", class }
|
||||||
|
style="color: hsl(var(--foreground)); font-family: var(--font-body, 'Inter', system-ui, sans-serif);"
|
||||||
|
>
|
||||||
|
@templ.Raw(text)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
68
text_override_templ.go
Normal file
68
text_override_templ.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1020
|
||||||
|
package main
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// cyberpunkTextComponent renders the override for the built-in text block:
|
||||||
|
// shadcn `prose` invert with cyberpunk leading and accent inline code.
|
||||||
|
func cyberpunkTextComponent(text, class string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
var templ_7745c5c3_Var2 = []any{"cyberpunk-prose prose prose-invert max-w-none leading-relaxed", class}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `text_override.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" style=\"color: hsl(var(--foreground)); font-family: var(--font-body, 'Inter', system-ui, sans-serif);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.Raw(text).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
Loading…
x
Reference in New Issue
Block a user