Added support for featured videos and optionally removing features from article header

This commit is contained in:
Jeremy Karst 2026-01-06 22:09:12 -05:00
parent d3ab5f343d
commit cc38f170f5
9 changed files with 476 additions and 92 deletions

View file

@ -5,11 +5,10 @@ date = 2025-02-27T07:07:07-05:00
draft = false draft = false
categories = ['thoughts'] categories = ['thoughts']
tags = ['hugo', 'web'] tags = ['hugo', 'web']
disableToc = true
showFeature = false
+++ +++
First Post!!!
<!--more-->
## Social anxiety or introvert, chicken or egg? ## Social anxiety or introvert, chicken or egg?
I have been an introvert for as long as I can remember, and while I don't want this to turn into a self-help post, suffice it to say that I have never made a personal website primarily because it makes the hairs on the back of my neck stand up. Let today be the day that I face my fears and finally launch this site. I have been an introvert for as long as I can remember, and while I don't want this to turn into a self-help post, suffice it to say that I have never made a personal website primarily because it makes the hairs on the back of my neck stand up. Let today be the day that I face my fears and finally launch this site.

View file

@ -7,9 +7,6 @@ categories = ['references']
tags = ['hugo', 'markdown'] tags = ['hugo', 'markdown']
+++ +++
This cheatsheet is intended as a quick reference and showcase of the markdown syntax used in Hugo and Congo.
<!--more-->
{{< lead >}} {{< lead >}}
This cheatsheet is intended as a quick reference and showcase of the markdown syntax used in Hugo and Congo. This cheatsheet is intended as a quick reference and showcase of the markdown syntax used in Hugo and Congo.
{{< /lead >}} {{< /lead >}}

View file

@ -1,57 +0,0 @@
---
title: "TOC Disable Example"
description: "Example showing how to disable the table of contents in markdown files"
date: 2025-01-27
draft: false
categories: ['examples']
tags: ['toc', 'markdown', 'hugo']
disableToc: true # This disables the table of contents for this page
---
{{< lead >}}
This page demonstrates how to disable the table of contents in markdown files.
{{< /lead >}}
## How to Disable TOC
To disable the table of contents in any markdown file (including `index.md` files), simply add the `disableToc: true` parameter to the front matter:
```yaml
---
title: "Your Page Title"
description: "Your page description"
disableToc: true # This disables the TOC
---
```
## Usage Examples
### For Individual Pages
Add `disableToc: true` to the front matter of any markdown file to disable the TOC for that specific page.
### For Index Pages
For `index.md` files that serve as list pages, you can also use the `cascade` parameter to apply the setting to all pages in that section:
```yaml
---
title: "Section Title"
cascade:
disableToc: true # Disables TOC for all pages in this section
---
```
## How It Works
When `disableToc: true` is set:
1. The table of contents will not be displayed on the page
2. The TOC JavaScript will not be loaded (improving performance)
3. The TOC highlighting functionality will be disabled
## Note
This feature works for both:
- Individual article pages (using the `single.html` template)
- List/index pages (using the `list.html` template)
The TOC will still be generated by Hugo, but it won't be displayed or have JavaScript functionality when disabled.

View file

@ -7,9 +7,6 @@ categories = ['references']
tags = ['LSI', 'TrueNAS Scale', 'Broadcom', 'Update', 'SAS3Flash', 'HBA'] tags = ['LSI', 'TrueNAS Scale', 'Broadcom', 'Update', 'SAS3Flash', 'HBA']
+++ +++
Navigating Broadcom and LSI HBA updates on TrueNAS Scale.
<!--more-->
{{< lead >}} {{< lead >}}
Navigating Broadcom and LSI HBA updates on TrueNAS Scale. Navigating Broadcom and LSI HBA updates on TrueNAS Scale.
{{< /lead >}} {{< /lead >}}

View file

@ -1,7 +1,11 @@
<article class="mt-6 flex max-w-prose flex-row"> <article class="mt-6 flex max-w-prose flex-row">
{{- $images := $.Resources.ByType "image" }} {{- $images := $.Resources.ByType "image" }}
{{- $videos := $.Resources.ByType "video" }}
{{- $thumbnail := $images.GetMatch (.Params.thumbnail | default "*thumb*") }} {{- $thumbnail := $images.GetMatch (.Params.thumbnail | default "*thumb*") }}
{{- $feature := $images.GetMatch (.Params.feature | default "*feature*") | default $thumbnail }} {{- $feature := $images.GetMatch (.Params.feature | default "*feature*") | default $thumbnail }}
{{- if not $feature }}
{{- $feature = $videos.GetMatch (.Params.feature | default "*feature*") }}
{{- end }}
{{- with $feature }} {{- with $feature }}
<div class="flex-none pe-4 sm:pe-6 "> <div class="flex-none pe-4 sm:pe-6 ">
<a <a
@ -12,21 +16,37 @@
{{ end }}" {{ end }}"
aria-label="{{ $.Title | emojify }}" aria-label="{{ $.Title | emojify }}"
> >
<img {{ if eq .ResourceType "video" }}
alt="{{ $.Params.featureAlt | default $.Params.thumbnailAlt | default "" }}" <video
{{ if eq .MediaType.SubType "svg" }} class="w-24 rounded-md sm:w-40 max-h-[7.5rem]"
class="w-24 max-w-[6rem] max-h-[4.5rem] rounded-md sm:max-h-[7.5rem] sm:w-40 autoplay
sm:max-w-[10rem]" src="{{ .RelPermalink }}" loop
{{ else }} muted
class="w-24 rounded-md sm:w-40" srcset=" playsinline
{{- (.Fill "160x120 smart").RelPermalink }} {{ if $.Site.Params.enableImageLazyLoading | default true }}
160w, {{- (.Fill "320x240 smart").RelPermalink }} 2x" loading="lazy"
src="{{ (.Fill "160x120 smart").RelPermalink }}" width="160" height="120" {{ end }}
{{ end }} aria-label="{{ $.Params.featureAlt | default $.Params.thumbnailAlt | default "" }}"
{{ if $.Site.Params.enableImageLazyLoading | default true }} >
loading="lazy" <source src="{{ .RelPermalink }}" type="{{ .MediaType }}">
{{ end }} </video>
/> {{ else }}
<img
alt="{{ $.Params.featureAlt | default $.Params.thumbnailAlt | default "" }}"
{{ if eq .MediaType.SubType "svg" }}
class="w-24 max-w-[6rem] max-h-[4.5rem] rounded-md sm:max-h-[7.5rem] sm:w-40
sm:max-w-[10rem]" src="{{ .RelPermalink }}"
{{ else }}
class="w-24 rounded-md sm:w-40" srcset="
{{- (.Fill "160x120 smart").RelPermalink }}
160w, {{- (.Fill "320x240 smart").RelPermalink }} 2x"
src="{{ (.Fill "160x120 smart").RelPermalink }}" width="160" height="120"
{{ end }}
{{ if $.Site.Params.enableImageLazyLoading | default true }}
loading="lazy"
{{ end }}
/>
{{ end }}
</a> </a>
</div> </div>
{{- end }} {{- end }}
@ -70,7 +90,15 @@
</div> </div>
{{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }} {{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }}
<div class="prose py-1 dark:prose-invert"> <div class="prose py-1 dark:prose-invert">
{{ $summary := .Summary | emojify }} {{ $summary := "" }}
{{ $hasManualSummary := findRE "<!--\\s*more\\s*-->" .RawContent }}
{{ if $hasManualSummary }}
{{ $summary = .Summary | emojify }}
{{ else if .Description }}
{{ $summary = .Description | emojify }}
{{ else }}
{{ $summary = .Summary | emojify }}
{{ end }}
{{ $summary := replaceRE "<h[1-6][^>]*>(.*?)</h[1-6]>" "$1" $summary }} {{ $summary := replaceRE "<h[1-6][^>]*>(.*?)</h[1-6]>" "$1" $summary }}
{{ $summary := replaceRE "\\s*<br\\s*/?>" " " $summary }} {{ $summary := replaceRE "\\s*<br\\s*/?>" " " $summary }}
{{ $summary := replaceRE "\\s+" " " $summary }} {{ $summary := replaceRE "\\s+" " " $summary }}

View file

@ -0,0 +1,23 @@
{{ $video := .video }}
{{ $class := .class }}
{{ $lazy := .lazy }}
{{ $alt := .alt }}
{{ $autoplay := .autoplay | default true }}
{{ $loop := .loop | default true }}
{{ $muted := .muted | default true }}
{{ with $video }}
<video
{{ with $class }}class="{{ . }}"{{ end }}
{{ if $autoplay }}autoplay{{ end }}
{{ if $loop }}loop{{ end }}
{{ if $muted }}muted{{ end }}
playsinline
{{ with $lazy }}loading="lazy"{{ end }}
{{ with $alt }}aria-label="{{ . }}"{{ end }}
>
<source src="{{ .RelPermalink }}" type="{{ .MediaType }}">
{{ with $alt }}{{ . }}{{ end }}
</video>
{{ end }}

View file

@ -1,8 +1,12 @@
{{ define "main" }} {{ define "main" }}
{{ partial "background-images.html" . }} {{ partial "background-images.html" . }}
{{- $images := .Resources.ByType "image" }} {{- $images := .Resources.ByType "image" }}
{{- $videos := .Resources.ByType "video" }}
{{- $cover := $images.GetMatch (.Params.cover | default "*cover*") }} {{- $cover := $images.GetMatch (.Params.cover | default "*cover*") }}
{{- $feature := $images.GetMatch (.Params.feature | default "*feature*") | default $cover }} {{- $feature := $images.GetMatch (.Params.feature | default "*feature*") | default $cover }}
{{- if not $feature }}
{{- $feature = $videos.GetMatch (.Params.feature | default "*feature*") }}
{{- end }}
<article class="prose max-w-full dark:prose-invert glass"> <article class="prose max-w-full dark:prose-invert glass">
<header class="max-w-prose"> <header class="max-w-prose">
{{ if .Params.showBreadcrumbs | default (.Site.Params.article.showBreadcrumbs | default false) }} {{ if .Params.showBreadcrumbs | default (.Site.Params.article.showBreadcrumbs | default false) }}
@ -22,16 +26,22 @@
{{ partial "article-meta.html" (dict "context" . "scope" "single") }} {{ partial "article-meta.html" (dict "context" . "scope" "single") }}
</div> </div>
{{ end }} {{ end }}
{{ with $feature }} {{ if .Params.showFeature | default true }}
<div class="prose"> {{ with $feature }}
{{ $altText := $.Params.featureAlt | default $.Params.coverAlt | default "" }} <div class="prose">
{{ $class := "mb-6 rounded-md" }} {{ $altText := $.Params.featureAlt | default $.Params.coverAlt | default "" }}
{{ $webp := $.Page.Site.Params.enableImageWebp | default true }} {{ $class := "mb-6 rounded-md" }}
{{ partial "picture.html" (dict "img" . "alt" $altText "class" $class "lazy" false "webp" $webp) }} {{ if eq .ResourceType "video" }}
{{ with $.Params.coverCaption }} {{ partial "video.html" (dict "video" . "alt" $altText "class" $class "lazy" false "autoplay" true "loop" true "muted" true) }}
<figcaption class="-mt-3 mb-6 text-center">{{ . | markdownify }}</figcaption> {{ else }}
{{ end }} {{ $webp := $.Page.Site.Params.enableImageWebp | default true }}
</div> {{ partial "picture.html" (dict "img" . "alt" $altText "class" $class "lazy" false "webp" $webp) }}
{{ end }}
{{ with $.Params.coverCaption }}
<figcaption class="-mt-3 mb-6 text-center">{{ . | markdownify }}</figcaption>
{{ end }}
</div>
{{ end }}
{{ end }} {{ end }}
</header> </header>
<section class="prose mt-0 flex max-w-full flex-col dark:prose-invert lg:flex-row"> <section class="prose mt-0 flex max-w-full flex-col dark:prose-invert lg:flex-row">

View file

@ -0,0 +1,387 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>Matrix Synapse Docker Guide</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700;800&family=Archivo+Black&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
margin: 0;
padding: 0;
overflow: hidden;
}
body {
background: #0a0a0a;
margin: 0;
padding: 0;
overflow: hidden;
}
#scaler {
width: 1280px;
height: 720px;
transform-origin: 0 0;
}
.thumbnail {
width: 1280px;
height: 720px;
background: linear-gradient(135deg, #0d1117 0%, #1a1f2e 50%, #0d1117 100%);
position: relative;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.8);
}
/* Animated grid background */
.grid-bg {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(0, 255, 159, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 255, 159, 0.03) 1px, transparent 1px);
background-size: 40px 40px;
animation: gridPulse 3s ease-in-out infinite;
}
@keyframes gridPulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
/* Glowing orbs */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.4;
animation: float 8s ease-in-out infinite;
}
.orb-1 {
width: 400px;
height: 400px;
background: radial-gradient(circle, #00ff9f 0%, transparent 70%);
top: -100px;
right: -100px;
animation-delay: 0s;
}
.orb-2 {
width: 300px;
height: 300px;
background: radial-gradient(circle, #00bfff 0%, transparent 70%);
bottom: -80px;
left: -80px;
animation-delay: 2s;
}
.orb-3 {
width: 250px;
height: 250px;
background: radial-gradient(circle, #7c3aed 0%, transparent 70%);
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation-delay: 4s;
}
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -30px) scale(1.1); }
66% { transform: translate(-30px, 30px) scale(0.9); }
}
/* Content container */
.content {
position: relative;
z-index: 10;
height: 100%;
display: flex;
flex-direction: column;
padding: 60px 80px;
}
/* Matrix logo section */
.logo-section {
display: flex;
align-items: center;
gap: 24px;
margin-bottom: 40px;
}
.matrix-logo {
width: 90px;
height: 90px;
background: linear-gradient(135deg, #00ff9f 0%, #00bfff 100%);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
font-family: 'JetBrains Mono', monospace;
font-size: 42px;
font-weight: 800;
color: #0a0a0a;
box-shadow: 0 8px 32px rgba(0, 255, 159, 0.3);
position: relative;
overflow: hidden;
}
.matrix-logo::before {
content: '';
position: absolute;
inset: -2px;
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.3) 50%, transparent 70%);
animation: shine 3s infinite;
}
@keyframes shine {
0% { transform: translateX(-100%) rotate(45deg); }
100% { transform: translateX(200%) rotate(45deg); }
}
.matrix-logo-text {
font-family: 'Archivo Black', sans-serif;
font-size: 36px;
color: #ffffff;
letter-spacing: -1px;
text-transform: uppercase;
}
/* Main title */
.main-title {
font-family: 'Archivo Black', sans-serif;
font-size: 82px;
line-height: 0.95;
color: #ffffff;
margin-bottom: 30px;
letter-spacing: -3px;
text-transform: uppercase;
}
.title-line-1 {
display: block;
background: linear-gradient(90deg, #ffffff 0%, #00ff9f 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.title-line-2 {
display: block;
color: #ffffff;
margin-left: 120px;
}
/* Tech badges */
.tech-badges {
display: flex;
gap: 16px;
margin-bottom: auto;
}
.badge {
padding: 12px 24px;
background: rgba(0, 63, 39, 0.9);
border: 2px solid rgba(0, 255, 159, 0.3);
border-radius: 8px;
font-family: 'JetBrains Mono', monospace;
font-size: 18px;
font-weight: 700;
color: #00ff9f;
text-transform: uppercase;
letter-spacing: 1px;
backdrop-filter: blur(10px);
box-shadow: 0 4px 16px rgba(0, 255, 159, 0.1);
}
/* Docker compose visual */
.docker-visual {
position: absolute;
right: 80px;
bottom: 80px;
width: 420px;
}
.terminal-window {
background: rgba(13, 17, 23, 0.95);
border: 1px solid rgba(0, 255, 159, 0.2);
border-radius: 12px;
padding: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
}
.terminal-header {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
.terminal-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.dot-1 { background: #ff5f56; }
.dot-2 { background: #ffbd2e; }
.dot-3 { background: #27c93f; }
.terminal-content {
font-family: 'JetBrains Mono', monospace;
font-size: 16px;
line-height: 1.6;
}
.terminal-line {
display: flex;
margin-bottom: 8px;
}
.terminal-prompt {
color: #00ff9f;
margin-right: 8px;
}
.terminal-command {
color: #00bfff;
}
.terminal-comment {
color: #6e7681;
}
.terminal-yaml {
color: #ffffff;
}
.yaml-key {
color: #ff7b72;
}
.yaml-value {
color: #a5d6ff;
}
.cursor {
display: inline-block;
width: 10px;
height: 20px;
background: #00ff9f;
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% { opacity: 0; }
}
/* Decorative elements */
.deco-line {
position: absolute;
height: 2px;
background: linear-gradient(90deg, transparent, #00ff9f, transparent);
opacity: 0.3;
}
.deco-line-1 {
width: 300px;
top: 180px;
left: 80px;
}
.deco-line-2 {
width: 200px;
bottom: 200px;
right: 520px;
}
</style>
</head>
<body>
<div id="scaler">
<div class="thumbnail">
<div class="grid-bg"></div>
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<div class="deco-line deco-line-1"></div>
<div class="deco-line deco-line-2"></div>
<div class="content">
<div class="logo-section">
<div class="matrix-logo">[m]</div>
<div class="matrix-logo-text">Synapse</div>
</div>
<h1 class="main-title">
<span class="title-line-1">Self-Host</span>
<span class="title-line-2">Your Server</span>
</h1>
<div class="tech-badges">
<div class="badge">Docker</div>
<div class="badge">Compose</div>
<div class="badge">Matrix</div>
</div>
</div>
<div class="docker-visual">
<div class="terminal-window">
<div class="terminal-header">
<div class="terminal-dot dot-1"></div>
<div class="terminal-dot dot-2"></div>
<div class="terminal-dot dot-3"></div>
</div>
<div class="terminal-content">
<div class="terminal-line">
<span class="terminal-prompt">$</span>
<span class="terminal-command">docker compose up -d</span>
</div>
<div class="terminal-line">
<span class="terminal-comment"># Starting services...</span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"><span class="yaml-key">synapse:</span></span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"> <span class="yaml-key">image:</span> <span class="yaml-value">matrixdotorg/synapse</span></span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"> <span class="yaml-key">ports:</span></span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"> - <span class="yaml-value">8008:8008</span></span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"> <span class="yaml-key">volumes:</span></span>
</div>
<div class="terminal-line">
<span class="terminal-yaml"> - <span class="yaml-value">./data:/data</span><span class="cursor"></span></span>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function scaleContent() {
const scaler = document.getElementById('scaler');
const scaleX = window.innerWidth / 1280;
const scaleY = window.innerHeight / 720;
const scale = Math.min(scaleX, scaleY);
scaler.style.transform = `scale(${scale})`;
}
scaleContent();
window.addEventListener('resize', scaleContent);
</script>
</body>
</html>