mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-12 18:15:31 +00:00
feat: add Dev HUD & 15 Obsidian callouts; more collection filters
Dev tools - Add HBX Dev HUD (floating button + panel) with live log viewer - Add get-build-id helper to dedupe logs per build (during `hugo server` refreshes) Callouts - Implement Obsidian-compatible callouts (15+ types) with shared renderer - Add i18n title translations (en, es, fr) for all callout types - Deprecate `callout` shortcode with suppressible warning; keep working - Migrate starters to Markdown callouts (>[!TYPE]) and update docs Logging - Centralize logging in partial `functions/logger` with info/warn/error levels - Use fmt.Warnidf for suppressible warnings (consistent IDs) - Only show info in Dev HUD/JSON (no longer use warnf for info logging); warn/error still printed in CLI - Include source metadata and build ID on each entry Blocks – Collection - Add support for `content.filters.kinds` - Add `content.filters.tags` (array) alongside existing `filters.tag` (string) Build/Tailwind/JS - Fix missing Tailwind v4 styles in Hugo render: include preact-built assets, extract classes from JSX/TSX to `@sources` - Load Preact client per block, publish source maps in dev; on-demand Alpine CI - Re-implement fast fails with panicOnWarning now that info logging as warnf removed - Fix package splitter change detection: strip trailing slashes; collapse paths to starter roots Starters/Docs - Restructure courses under `courses/hugo-blox/` - Update menus and landing pages - Replace deprecated callout usage in content and docs - Replace missing hero icon `download` with `arrow-down-tray` in academic-cv guide Icons - Improve icon resolver: default pack inference, better fallback, and precise missing-icon warnings (with page ref)
This commit is contained in:
1 parent
96f96636fc
commit
787ac94b5d
55 files changed
+1617
-543
No files matched your search
@@ -100,4 +100,4 @@ jobs:
|
||||
|
||||
- name: Build ${{ matrix.starter.name }}
|
||||
working-directory: ${{ matrix.starter.path }}
|
||||
run: hugo --minify
|
||||
run: hugo --minify --panicOnWarning
|
||||
@@ -98,5 +98,5 @@ jobs:
|
||||
extended: true
|
||||
|
||||
- name: Build
|
||||
run: hugo --minify --templateMetrics --templateMetricsHints
|
||||
run: hugo --minify --panicOnWarning --templateMetrics --templateMetricsHints
|
||||
working-directory: test
|
||||
@@ -83,14 +83,25 @@ jobs:
|
||||
FIRST=true
|
||||
# tj-actions outputs space-separated list
|
||||
for DIR in $CHANGED_DIRS; do
|
||||
# Normalize potential trailing slash
|
||||
DIR="${DIR%/}"
|
||||
|
||||
# Collapse to the starter root (first two path segments), e.g. starters/landing-page
|
||||
IFS='/' read -r SEG1 SEG2 _ <<< "$DIR"
|
||||
if [[ -n "$SEG1" && -n "$SEG2" ]]; then
|
||||
ROOT="$SEG1/$SEG2"
|
||||
else
|
||||
ROOT="$DIR"
|
||||
fi
|
||||
|
||||
# Only process if it's a known starter
|
||||
if [[ -n "${STARTER_MAP[$DIR]}" ]]; then
|
||||
if [[ -n "${STARTER_MAP[$ROOT]}" ]]; then
|
||||
if [ "$FIRST" = true ]; then
|
||||
FIRST=false
|
||||
else
|
||||
JSON+=","
|
||||
JSON+=","
|
||||
fi
|
||||
JSON+="{\"local_path\":\"$DIR\",\"split_repository\":\"${STARTER_MAP[$DIR]}\"}"
|
||||
JSON+="{\"local_path\":\"$ROOT\",\"split_repository\":\"${STARTER_MAP[$ROOT]}\"}"
|
||||
fi
|
||||
done
|
||||
JSON+="]"
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
{{ end }}
|
||||
|
||||
{{/* Query */}}
|
||||
{{ $query := site.RegularPages }}
|
||||
{{ $query := site.Pages }}
|
||||
{{ $archive_page := "" }}
|
||||
|
||||
{{/* Filters */}}
|
||||
{{ $kinds := $block.content.filters.kinds | default (slice "page") }}
|
||||
{{ $query = where $query "Kind" "in" $kinds }}
|
||||
|
||||
{{ if $block.content.page_type }}
|
||||
{{ $query = where $query "Type" $block.content.page_type }}
|
||||
{{ $archive_page = site.GetPage "Section" $block.content.page_type }}
|
||||
@@ -31,6 +34,20 @@
|
||||
{{ $main_folder := index $folders 0 }}
|
||||
{{ $archive_page = site.GetPage "Section" $main_folder }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.tags }}
|
||||
{{ $tags := $block.content.filters.tags }}
|
||||
{{ $selected := slice }}
|
||||
{{ range $tags }}
|
||||
{{ $tag_page := site.GetPage (printf "tags/%s" (urlize .)) }}
|
||||
{{ if $tag_page }}
|
||||
{{ $selected = $selected | union $tag_page.Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $selected) 0 }}
|
||||
{{ $query = $query | intersect $selected }}
|
||||
{{ $archive_page = site.GetPage (printf "tags/%s" (urlize (index $tags 0))) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.tag }}
|
||||
{{ $archive_page = site.GetPage (printf "tags/%s" (urlize $block.content.filters.tag)) }}
|
||||
{{ $query = $query | intersect $archive_page.Pages }}
|
||||
|
||||
@@ -26,19 +26,52 @@
|
||||
- id: related
|
||||
translation: Related
|
||||
|
||||
# Callouts
|
||||
# Callouts - All Obsidian-compatible types
|
||||
- id: callout_note
|
||||
translation: Note
|
||||
|
||||
- id: callout_abstract
|
||||
translation: Abstract
|
||||
|
||||
- id: callout_summary
|
||||
translation: Summary
|
||||
|
||||
- id: callout_info
|
||||
translation: Info
|
||||
|
||||
- id: callout_todo
|
||||
translation: Todo
|
||||
|
||||
- id: callout_tip
|
||||
translation: Tip
|
||||
|
||||
- id: callout_important
|
||||
translation: Important
|
||||
- id: callout_success
|
||||
translation: Success
|
||||
|
||||
- id: callout_question
|
||||
translation: Question
|
||||
|
||||
- id: callout_warning
|
||||
translation: Warning
|
||||
|
||||
- id: callout_failure
|
||||
translation: Failure
|
||||
|
||||
- id: callout_danger
|
||||
translation: Danger
|
||||
|
||||
- id: callout_bug
|
||||
translation: Bug
|
||||
|
||||
- id: callout_example
|
||||
translation: Example
|
||||
|
||||
- id: callout_quote
|
||||
translation: Quote
|
||||
|
||||
- id: callout_important
|
||||
translation: Important
|
||||
|
||||
- id: callout_caution
|
||||
translation: Caution
|
||||
|
||||
|
||||
@@ -26,19 +26,52 @@
|
||||
- id: related
|
||||
translation: Relacionado
|
||||
|
||||
# Callouts
|
||||
# Callouts - All Obsidian-compatible types
|
||||
- id: callout_note
|
||||
translation: Nota
|
||||
|
||||
- id: callout_abstract
|
||||
translation: Resumen
|
||||
|
||||
- id: callout_summary
|
||||
translation: Resumen
|
||||
|
||||
- id: callout_info
|
||||
translation: Info
|
||||
|
||||
- id: callout_todo
|
||||
translation: Pendiente
|
||||
|
||||
- id: callout_tip
|
||||
translation: Consejo
|
||||
|
||||
- id: callout_important
|
||||
translation: Importante
|
||||
- id: callout_success
|
||||
translation: Éxito
|
||||
|
||||
- id: callout_question
|
||||
translation: Pregunta
|
||||
|
||||
- id: callout_warning
|
||||
translation: Advertencia
|
||||
|
||||
- id: callout_failure
|
||||
translation: Fallo
|
||||
|
||||
- id: callout_danger
|
||||
translation: Peligro
|
||||
|
||||
- id: callout_bug
|
||||
translation: Error
|
||||
|
||||
- id: callout_example
|
||||
translation: Ejemplo
|
||||
|
||||
- id: callout_quote
|
||||
translation: Cita
|
||||
|
||||
- id: callout_important
|
||||
translation: Importante
|
||||
|
||||
- id: callout_caution
|
||||
translation: Precaución
|
||||
|
||||
|
||||
@@ -26,19 +26,52 @@
|
||||
- id: related
|
||||
translation: Sur le même sujet
|
||||
|
||||
# Callouts
|
||||
# Callouts - All Obsidian-compatible types
|
||||
- id: callout_note
|
||||
translation: Note
|
||||
|
||||
- id: callout_abstract
|
||||
translation: Résumé
|
||||
|
||||
- id: callout_summary
|
||||
translation: Résumé
|
||||
|
||||
- id: callout_info
|
||||
translation: Info
|
||||
|
||||
- id: callout_todo
|
||||
translation: À faire
|
||||
|
||||
- id: callout_tip
|
||||
translation: Conseil
|
||||
|
||||
- id: callout_important
|
||||
translation: Important
|
||||
- id: callout_success
|
||||
translation: Succès
|
||||
|
||||
- id: callout_question
|
||||
translation: Question
|
||||
|
||||
- id: callout_warning
|
||||
translation: Attention
|
||||
|
||||
- id: callout_failure
|
||||
translation: Échec
|
||||
|
||||
- id: callout_danger
|
||||
translation: Danger
|
||||
|
||||
- id: callout_bug
|
||||
translation: Bug
|
||||
|
||||
- id: callout_example
|
||||
translation: Exemple
|
||||
|
||||
- id: callout_quote
|
||||
translation: Citation
|
||||
|
||||
- id: callout_important
|
||||
translation: Important
|
||||
|
||||
- id: callout_caution
|
||||
translation: Prudence
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{{/*
|
||||
Get Build ID - Returns a consistent build identifier for the current build cycle.
|
||||
This is cached across the entire build to prevent log duplication during dev rebuilds.
|
||||
*/}}
|
||||
{{- printf "%d" now.Unix -}}
|
||||
@@ -20,10 +20,33 @@
|
||||
{{ with index (index site.Data.icons.hb "icons") $name -}}
|
||||
{{ $icon = . }}
|
||||
{{ else }}
|
||||
{{ with resources.GetMatch (path.Join "media" "icons" $pack (printf "%s.svg" $name)) }}
|
||||
{{- $prefix := printf "media/icons/%s/" $pack -}}
|
||||
{{- $icon_path := printf "%s%s.svg" $prefix $name -}}
|
||||
{{- if not (fileExists (printf "assets/%s" $icon_path)) -}}
|
||||
{{ $pageRef := "unknown location" }}
|
||||
{{ if .Page }}
|
||||
{{ if .Page.File }}
|
||||
{{ if .Page.File.Path }}
|
||||
{{ $pageRef = .Page.File.Path }}
|
||||
{{ else if .Page.RelPermalink }}
|
||||
{{ $pageRef = .Page.RelPermalink }}
|
||||
{{ end }}
|
||||
{{ else if .Page.RelPermalink }}
|
||||
{{ $pageRef = .Page.RelPermalink }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ partial "functions/logger" (dict
|
||||
"page" .Page
|
||||
"level" "warn"
|
||||
"id" (printf "missing-icon-%s-%s" $pack $name)
|
||||
"message" (printf "The icon `%s` was not found in `assets/media/icons/%s/`. Referenced in: %s" $name $pack $pageRef)
|
||||
)
|
||||
}}
|
||||
{{- end -}}
|
||||
{{- $icon := resources.Get $icon_path -}}
|
||||
{{ if $icon }}
|
||||
{{ $icon = .Content }}
|
||||
{{else}}
|
||||
{{ partial "functions/logger" (dict "page" $.Page "level" "warn" "message" (printf "The icon `%s.svg` was not found in your `assets/media/icons/%s/` folder" $name $pack)) }}
|
||||
{{ else }}
|
||||
{{ $icon = index (index site.Data.icons.brands "icons") "hugo" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
{{/* Hugo Blox Logger: centralized logging helper
|
||||
Usage: partial "functions/logger" (dict "page" . "level" "warn" "message" (printf "..." args...))
|
||||
- level: "warn" (default) or "error"
|
||||
- page: optional; current page. Defaults to dot. Used to detect adapter pages
|
||||
- adapter: optional bool to force adapter-safe mode (e.g., from _content.gotmpl)
|
||||
*/}}
|
||||
{{- $level := lower (.level | default "warn") -}}
|
||||
{{- $msg := .message | default "" -}}
|
||||
{{- $page := .page | default . -}}
|
||||
{{- $forceAdapter := .adapter | default false -}}
|
||||
{{- $isDev := or hugo.IsServer (eq hugo.Environment "development") -}}
|
||||
{{/*
|
||||
Hugo Blox Logger: A pragmatic, centralized logging helper for Hugo.
|
||||
|
||||
{{/* Detect adapter-generated pages via explicit flag or page param */}}
|
||||
{{- $isAdapter := $forceAdapter -}}
|
||||
{{- if not $isAdapter -}}
|
||||
{{- with $page -}}
|
||||
{{- with .Params -}}
|
||||
{{- if or (index . "hb_adapter") (eq (printf "%T" .) "map[string]interface {}" | and (index . "hb_adapter")) -}}
|
||||
{{- $isAdapter = true -}}
|
||||
{{- end -}}
|
||||
This partial is the single source of truth for all build-time logging.
|
||||
Hugo's template logging is limited to WARN and ERROR. INFO is only shown in the Dev HUD/JSON, not the CLI.
|
||||
|
||||
Usage:
|
||||
{{ partial "functions/logger" (dict
|
||||
"page" .
|
||||
"level" "info" | "warn" | "error" (default: "info")
|
||||
"message" "Your log message here."
|
||||
"id" "a-unique-id" (optional, for suppressible warnings)
|
||||
"source" (dict "type" "shortcode|partial|template" "name" "callout|sections" ) (optional)
|
||||
) }}
|
||||
*/}}
|
||||
{{- $level := lower (.level | default "info") -}}
|
||||
{{- $message := .message | default "" -}}
|
||||
{{- $id := .id | default "" -}}
|
||||
{{- $source := .source | default dict -}}
|
||||
{{- $page := .page | default . -}}
|
||||
{{- $isDev := hugo.IsServer -}}
|
||||
|
||||
{{/* Store log entry for future debug widget/JSON, regardless of environment. */}}
|
||||
{{- with $page -}}
|
||||
{{- with .Store -}}
|
||||
{{- $buildId := partialCached "functions/get-build-id.html" "hbx-build-id" -}}
|
||||
{{- $log_entry := dict "level" $level "message" $message "page" ($.RelPermalink | default "/") "timestamp" now.Unix "id" $id "source" $source "buildId" $buildId -}}
|
||||
{{- $current_logs := .Get "hbx_logs" | default slice -}}
|
||||
{{- $currentBuildId := .Get "hbx_build_id" | default "" -}}
|
||||
{{/* Clear logs if this is a new build cycle */}}
|
||||
{{- if ne $currentBuildId $buildId -}}
|
||||
{{- .Set "hbx_build_id" $buildId -}}
|
||||
{{- $current_logs = slice -}}
|
||||
{{- end -}}
|
||||
{{- .Set "hbx_logs" ($current_logs | append $log_entry) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Store log entry for future debug widget */}}
|
||||
{{- with $page -}}
|
||||
{{- $log_entry := dict "level" $level "message" $msg "page" ($.RelPermalink | default "/") "timestamp" now.Unix -}}
|
||||
{{- $current_logs := .Store.Get "hbx_logs" | default slice -}}
|
||||
{{- .Store.Set "hbx_logs" ($current_logs | append $log_entry) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Multi-tier logging output */}}
|
||||
{{- if $isDev -}}
|
||||
{{/* Console logging for immediate dev feedback */}}
|
||||
<script>console.log('[HBX:{{ $level }}]', {{ $msg | jsonify }});</script>
|
||||
{{- end -}}
|
||||
|
||||
{{/* Emit to CLI using fmt with proper severity. Do not print INFO to CLI. */}}
|
||||
{{- if eq $level "error" -}}
|
||||
{{- if not $isAdapter -}}
|
||||
{{- errorf "%s" $msg -}}
|
||||
{{- fmt.Errorf "HBX ERROR: %s" $message -}}
|
||||
{{- else if eq $level "warn" -}}
|
||||
{{- if $id -}}
|
||||
{{- fmt.Warnidf $id "HBX WARN: %s" $message -}}
|
||||
{{- else -}}
|
||||
{{- if $isDev -}}<!-- HBX log:error: {{ $msg }} -->{{- end -}}
|
||||
{{- fmt.Warnf "HBX WARN: %s" $message -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if not $isAdapter -}}
|
||||
{{- warnf "%s" $msg -}}
|
||||
{{- else -}}
|
||||
{{- if $isDev -}}<!-- HBX log:warn: {{ $msg }} -->{{- end -}}
|
||||
{{- end -}}
|
||||
{{/* info: no CLI output; visible via Dev HUD/JSON only */}}
|
||||
{{- end -}}
|
||||
{{- "" -}}
|
||||
@@ -0,0 +1,155 @@
|
||||
{{/*
|
||||
Hugo Blox Builder Shared Callout Renderer
|
||||
|
||||
Renders callouts with consistent styling for both shortcodes and render hooks.
|
||||
|
||||
Parameters:
|
||||
- type: The callout type (note, tip, important, warning, caution, or custom icon name)
|
||||
- content: The callout content (HTML)
|
||||
- title: Optional title for the callout
|
||||
- source: "shortcode" or "renderhook" for context
|
||||
*/}}
|
||||
|
||||
{{ $type := (lower .type) | default "note" }}
|
||||
{{ $content := .content }}
|
||||
{{ $title := .title }}
|
||||
{{ if or (not $title) (eq $title "") }}
|
||||
{{/* Try i18n translation with "callout_" prefix, fall back to title case */}}
|
||||
{{ $i18n_key := printf "callout_%s" $type }}
|
||||
{{ $title = or (i18n $i18n_key) (title $type) }}
|
||||
{{ end }}
|
||||
{{ $source := .source | default "shortcode" }}
|
||||
|
||||
{{/* Define icon and styling mappings for all Obsidian callout types */}}
|
||||
{{ $alertConfigs := dict
|
||||
"note" (dict
|
||||
"icon" "pencil"
|
||||
"class" "bg-blue-100 dark:bg-blue-900"
|
||||
"textClass" "text-blue-600 dark:text-blue-300"
|
||||
"borderClass" "border-blue-500"
|
||||
)
|
||||
"abstract" (dict
|
||||
"icon" "clipboard-document-list"
|
||||
"class" "bg-cyan-100 dark:bg-cyan-900"
|
||||
"textClass" "text-cyan-600 dark:text-cyan-300"
|
||||
"borderClass" "border-cyan-500"
|
||||
)
|
||||
"summary" (dict
|
||||
"icon" "clipboard-document-list"
|
||||
"class" "bg-cyan-100 dark:bg-cyan-900"
|
||||
"textClass" "text-cyan-600 dark:text-cyan-300"
|
||||
"borderClass" "border-cyan-500"
|
||||
)
|
||||
"info" (dict
|
||||
"icon" "information-circle"
|
||||
"class" "bg-blue-100 dark:bg-blue-900"
|
||||
"textClass" "text-blue-600 dark:text-blue-300"
|
||||
"borderClass" "border-blue-500"
|
||||
)
|
||||
"todo" (dict
|
||||
"icon" "check-circle"
|
||||
"class" "bg-blue-100 dark:bg-blue-900"
|
||||
"textClass" "text-blue-600 dark:text-blue-300"
|
||||
"borderClass" "border-blue-500"
|
||||
)
|
||||
"tip" (dict
|
||||
"icon" "light-bulb"
|
||||
"class" "bg-emerald-100 dark:bg-emerald-900"
|
||||
"textClass" "text-emerald-600 dark:text-emerald-300"
|
||||
"borderClass" "border-emerald-500"
|
||||
)
|
||||
"success" (dict
|
||||
"icon" "check-circle"
|
||||
"class" "bg-green-100 dark:bg-green-900"
|
||||
"textClass" "text-green-600 dark:text-green-300"
|
||||
"borderClass" "border-green-500"
|
||||
)
|
||||
"question" (dict
|
||||
"icon" "question-mark-circle"
|
||||
"class" "bg-yellow-100 dark:bg-yellow-900"
|
||||
"textClass" "text-yellow-700 dark:text-yellow-300"
|
||||
"borderClass" "border-yellow-500"
|
||||
)
|
||||
"warning" (dict
|
||||
"icon" "exclamation-triangle"
|
||||
"class" "bg-orange-100 dark:bg-orange-900"
|
||||
"textClass" "text-orange-600 dark:text-orange-300"
|
||||
"borderClass" "border-orange-500"
|
||||
)
|
||||
"failure" (dict
|
||||
"icon" "x-circle"
|
||||
"class" "bg-red-100 dark:bg-red-900"
|
||||
"textClass" "text-red-600 dark:text-red-300"
|
||||
"borderClass" "border-red-500"
|
||||
)
|
||||
"danger" (dict
|
||||
"icon" "exclamation-triangle"
|
||||
"class" "bg-red-100 dark:bg-red-900"
|
||||
"textClass" "text-red-600 dark:text-red-300"
|
||||
"borderClass" "border-red-500"
|
||||
)
|
||||
"bug" (dict
|
||||
"icon" "bug-ant"
|
||||
"class" "bg-red-100 dark:bg-red-900"
|
||||
"textClass" "text-red-600 dark:text-red-300"
|
||||
"borderClass" "border-red-500"
|
||||
)
|
||||
"example" (dict
|
||||
"icon" "beaker"
|
||||
"class" "bg-purple-100 dark:bg-purple-900"
|
||||
"textClass" "text-purple-600 dark:text-purple-300"
|
||||
"borderClass" "border-purple-500"
|
||||
)
|
||||
"quote" (dict
|
||||
"icon" "chat-bubble-left-right"
|
||||
"class" "bg-gray-100 dark:bg-gray-800"
|
||||
"textClass" "text-gray-600 dark:text-gray-300"
|
||||
"borderClass" "border-gray-500"
|
||||
)
|
||||
"important" (dict
|
||||
"icon" "exclamation-circle"
|
||||
"class" "bg-purple-100 dark:bg-purple-900"
|
||||
"textClass" "text-purple-600 dark:text-purple-300"
|
||||
"borderClass" "border-purple-500"
|
||||
)
|
||||
"caution" (dict
|
||||
"icon" "exclamation-triangle"
|
||||
"class" "bg-red-100 dark:bg-red-900"
|
||||
"textClass" "text-red-600 dark:text-red-300"
|
||||
"borderClass" "border-red-500"
|
||||
)
|
||||
}}
|
||||
|
||||
{{/* Get configuration for this callout type, or use fallback */}}
|
||||
{{ $config := index $alertConfigs $type }}
|
||||
{{ $icon := $type }}
|
||||
{{ $class := "bg-blue-100 dark:bg-blue-900" }}
|
||||
{{ $textClass := "text-blue-600 dark:text-blue-300" }}
|
||||
{{ $borderClass := "border-blue-500" }}
|
||||
|
||||
{{ if $config }}
|
||||
{{ $icon = $config.icon }}
|
||||
{{ $class = $config.class }}
|
||||
{{ $textClass = $config.textClass }}
|
||||
{{ $borderClass = $config.borderClass }}
|
||||
{{ end }}
|
||||
|
||||
<div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 {{ $class }} {{ $borderClass }}"
|
||||
data-callout="{{ $type }}"
|
||||
data-callout-metadata="">
|
||||
<span class="callout-icon pr-3 pt-1 {{ $textClass }}">
|
||||
{{ partial "functions/get_icon" (dict "name" $icon "attributes" "height=\"24\"") }}
|
||||
</span>
|
||||
<div class="callout-content dark:text-neutral-300">
|
||||
{{/* Display title if provided */}}
|
||||
{{- with $title -}}
|
||||
<div class="callout-title font-semibold mb-1">
|
||||
{{- . -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{/* Display content */}}
|
||||
<div class="callout-body">
|
||||
{{- $content -}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@
|
||||
{{- end -}}
|
||||
{{- $source = printf "data/pages/%s.yaml (sections_source)" $key -}}
|
||||
{{- else -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "warn" "message" (printf "HBX: sections_source '%s' not found under data/pages/" $key)) -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "warn" "id" (printf "sections-source-missing-%s" $key) "message" (printf "sections_source '%s' not found under data/pages/" $key) "source" (dict "type" "partial" "name" "hbx/sections")) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
{{- end -}}
|
||||
{{- $sec = $merged -}}
|
||||
{{- else -}}
|
||||
{{- partial "functions/logger" (dict "page" $ "level" "warn" "message" (printf "HBX: section ref '%s' not found under data/blocks/" $ref)) -}}
|
||||
{{- partial "functions/logger" (dict "page" $ "level" "warn" "id" (printf "section-ref-missing-%s" $ref) "message" (printf "HBX: section ref '%s' not found under data/blocks/" $ref) "source" (dict "type" "partial" "name" "hbx/sections")) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $resolved = $resolved | append $sec -}}
|
||||
@@ -98,8 +98,8 @@
|
||||
{{- end -}}
|
||||
|
||||
{{- if $sections -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "warn" "message" (printf "HBX: Using sections from %s for page %s (found %d sections)" $source $.RelPermalink (len $sections))) -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "info" "message" (printf "✅ Using sections from %s for page %s (found %d sections)" $source $.RelPermalink (len $sections)) "source" (dict "type" "partial" "name" "hbx/sections")) -}}
|
||||
{{- else -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "warn" "message" (printf "HBX: No sections found for page %s (checked %s)" $.RelPermalink $source)) -}}
|
||||
{{- partial "functions/logger" (dict "page" . "level" "warn" "id" (printf "sections-none-%s" $.RelPermalink) "message" (printf "HBX: No sections found for page %s (checked %s)" $.RelPermalink $source) "source" (dict "type" "partial" "name" "hbx/sections")) -}}
|
||||
{{- end -}}
|
||||
{{- return $sections -}}
|
||||
@@ -1,42 +1,67 @@
|
||||
{{/* Hugo Blox: Export Debug Logs for Future Debug Widget */}}
|
||||
{{/* This hook collects all HBX logs and exports them to a JSON file for tooling */}}
|
||||
|
||||
{{- if hugo.IsServer -}}
|
||||
{{/* Collect all logs from all pages */}}
|
||||
{{- $all_logs := slice -}}
|
||||
{{/* Hugo Blox: Export Debug Logs for Dev HUD (window var) */}}
|
||||
{{- if and hugo.IsServer (site.Params.hbx.log.export | default true) -}}
|
||||
{{- /* Collect all logs from all pages (site-wide) */ -}}
|
||||
{{- $entries := slice -}}
|
||||
{{- range .Site.Pages -}}
|
||||
{{- $page_logs := .Store.Get "hbx_logs" | default slice -}}
|
||||
{{- $all_logs = $all_logs | append $page_logs -}}
|
||||
{{- with .Store -}}
|
||||
{{- with .Get "hbx_logs" -}}
|
||||
{{- $entries = $entries | append . -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $all_logs -}}
|
||||
{{/* Create debug export as a resource for tooling */}}
|
||||
{{- $debug_data := dict "logs" $all_logs "build_time" now.Unix "hugo_version" hugo.Version -}}
|
||||
{{- $debug_json := $debug_data | jsonify -}}
|
||||
{{- $debug_resource := resources.FromString "hbx-debug.json" $debug_json -}}
|
||||
{{- $debug_resource.Publish -}}
|
||||
|
||||
{{/* Also emit to console for immediate visibility */}}
|
||||
<script>
|
||||
if (window.console && console.groupCollapsed) {
|
||||
console.groupCollapsed('[HBX] Build completed with {{ len $all_logs }} log entries');
|
||||
console.log('Debug data available at:', '/hbx-debug.json');
|
||||
{{ range $all_logs }}
|
||||
{{ if . }}
|
||||
{{ if reflect.IsMap . }}
|
||||
console.log(
|
||||
'[HBX:{{ if .level }}{{ .level }}{{ else }}info{{ end }}]',
|
||||
{{ if .message }}{{ .message | jsonify }}{{ else }}"No message"{{ end }},
|
||||
'page:',
|
||||
{{ if .page }}{{ .page | jsonify }}{{ else }}"unknown"{{ end }}
|
||||
);
|
||||
{{ else }}
|
||||
console.log('[HBX:info]', "Log entry with unexpected format", {{ . | jsonify }});
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
console.groupEnd();
|
||||
}
|
||||
</script>
|
||||
|
||||
{{- /* Summarize dependencies */ -}}
|
||||
{{- $deps := slice -}}
|
||||
{{- range $i, $d := hugo.Deps -}}
|
||||
{{- $dep := dict "path" $d.Path "version" $d.Version "vendor" $d.Vendor -}}
|
||||
{{- with $d.Owner -}}
|
||||
{{- $dep = merge $dep (dict "owner_path" .Path) -}}
|
||||
{{- end -}}
|
||||
{{- with $d.Replace -}}
|
||||
{{- $dep = merge $dep (dict "replace_path" .Path) -}}
|
||||
{{- end -}}
|
||||
{{- with $d.Time -}}
|
||||
{{- $dep = merge $dep (dict "time" (printf "%v" .)) -}}
|
||||
{{- end -}}
|
||||
{{- $deps = $deps | append $dep -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Detect blox-tailwind module version if present */ -}}
|
||||
{{- $bloxTW := dict -}}
|
||||
{{- range $deps -}}
|
||||
{{- $p := .path -}}
|
||||
{{- if and $p (in $p "blox-tailwind") -}}
|
||||
{{- $bloxTW = dict "path" .path "version" .version -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Current page information */ -}}
|
||||
{{- $filePath := "" -}}
|
||||
{{- with .File -}}
|
||||
{{- $filePath = .Path -}}
|
||||
{{- end -}}
|
||||
{{- $buildId := partialCached "functions/get-build-id.html" "hbx-build-id" -}}
|
||||
{{- $currentPage := dict
|
||||
"path" .RelPermalink
|
||||
"type" .Type
|
||||
"layout" (.Layout | default "")
|
||||
"kind" .Kind
|
||||
"section" .Section
|
||||
"title" .Title
|
||||
"file" $filePath
|
||||
"buildId" $buildId
|
||||
-}}
|
||||
|
||||
{{- $payload := dict
|
||||
"version" 1
|
||||
"generatedAt" (now.UTC.Format "2006-01-02T15:04:05Z07:00")
|
||||
"hugo" (dict "version" hugo.Version "goVersion" hugo.GoVersion)
|
||||
"deps" $deps
|
||||
"modules" (dict "bloxTailwind" $bloxTW)
|
||||
"entries" $entries
|
||||
"currentPage" $currentPage
|
||||
-}}
|
||||
<script>
|
||||
window.__HBX_LOGS__ = {{ $payload | jsonify | safeJS }};
|
||||
</script>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,298 @@
|
||||
{{/* Hugo Blox: Dev HUD (floating button + panel). Dev only. */}}
|
||||
{{- if hugo.IsServer -}}
|
||||
<script>
|
||||
(function(){
|
||||
try {
|
||||
const PLACEMENT = {{ (site.Params.hbx.log.placement | default "bottom-left") | jsonify }};
|
||||
const OPACITY = {{ (site.Params.hbx.log.opacity | default 1.0) | jsonify }};
|
||||
|
||||
const root = document.createElement('div');
|
||||
root.id = 'hbx-dev-hud-root';
|
||||
root.style.all = 'initial';
|
||||
root.style.position = 'fixed';
|
||||
root.style.zIndex = '2147483647';
|
||||
root.style.pointerEvents = 'none';
|
||||
root.style.bottom = '16px';
|
||||
if (PLACEMENT === 'bottom-right') root.style.right = '16px';
|
||||
else if (PLACEMENT === 'bottom-center') { root.style.left = '50%'; root.style.transform = 'translateX(-50%)'; }
|
||||
else root.style.left = '16px';
|
||||
|
||||
const shadow = root.attachShadow({ mode: 'open' });
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
:host { all: initial; }
|
||||
.btn { pointer-events: auto; position:relative; display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; border-radius:9999px; background:#111418; color:#e6eaf0; border:1px solid #2a2f38; box-shadow: 0 10px 20px rgba(0,0,0,.25); cursor:pointer; }
|
||||
.btn:hover { filter: brightness(1.1); }
|
||||
.badge { position:absolute; top:-4px; right:-4px; min-width:18px; height:18px; padding:0 4px; border-radius:9999px; background:#ef4444; color:white; font: 600 11px/18px system-ui, sans-serif; text-align:center; }
|
||||
.panel { pointer-events:auto; position:fixed; inset:auto auto 72px 16px; width:min(92vw, 1200px); max-height:min(80vh, 800px); background:#0d1117; color:#e6eaf0; border:1px solid #21262d; border-radius:12px; box-shadow: 0 20px 40px rgba(0,0,0,.4); overflow:hidden; display:none; }
|
||||
.panel.open { display:flex; flex-direction:column; }
|
||||
.panel-header { display:flex; align-items:center; gap:12px; padding:12px 16px; border-bottom:1px solid #21262d; background:#161b22; }
|
||||
.panel-header h2 { margin:0; font: 700 16px system-ui, sans-serif; letter-spacing:-0.01em; color:#f0f6fc; }
|
||||
.panel-header .close-btn { margin-left:auto; width:32px; height:32px; border:none; background:#21262d; color:#8b949e; border-radius:6px; cursor:pointer; font-size:16px; display:flex; align-items:center; justify-content:center; }
|
||||
.panel-header .close-btn:hover { background:#30363d; color:#f0f6fc; }
|
||||
.page-info { padding:16px; background:#0d1117; border-bottom:1px solid #21262d; }
|
||||
.page-info h3 { margin:0 0 12px 0; font: 600 14px system-ui, sans-serif; color:#f0f6fc; display:flex; align-items:center; gap:8px; }
|
||||
.page-info h3::before { content:'🔍'; font-size:16px; }
|
||||
.page-meta { display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:12px; }
|
||||
.meta-item { display:flex; flex-direction:column; gap:4px; }
|
||||
.meta-label { font: 500 11px system-ui, sans-serif; color:#8b949e; text-transform:uppercase; letter-spacing:0.5px; }
|
||||
.meta-value { font: 600 13px "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace; color:#79c0ff; background:#161b22; padding:6px 8px; border-radius:6px; border:1px solid #21262d; word-break:break-all; }
|
||||
.meta-value.type { color:#a5f3fc; }
|
||||
.meta-value.layout { color:#fbbf24; }
|
||||
.tabs { display:flex; background:#161b22; border-bottom:1px solid #21262d; }
|
||||
.tab { padding:12px 16px; font: 500 14px system-ui, sans-serif; color:#8b949e; cursor:pointer; border-bottom:2px solid transparent; }
|
||||
.tab:hover { color:#f0f6fc; }
|
||||
.tab.active { color:#58a6ff; border-bottom-color:#58a6ff; }
|
||||
.tab-content { flex:1; overflow:auto; }
|
||||
.env-info { padding:12px 16px; font: 500 11px system-ui, sans-serif; color:#8b949e; background:#0d1117; border-bottom:1px solid #21262d; display:flex; align-items:center; gap:16px; }
|
||||
.filters { display:flex; gap:12px; align-items:center; margin-left:auto; }
|
||||
.filters label { display:inline-flex; gap:6px; align-items:center; font: 500 12px system-ui, sans-serif; color:#c9d2e1; cursor:pointer; }
|
||||
.logs-content { overflow:auto; }
|
||||
.entry { display:grid; grid-template-columns:auto 1fr auto; gap:12px; padding:12px 16px; border-bottom:1px solid #21262d; }
|
||||
.entry:hover { background:#161b22; }
|
||||
.entry .lvl { font:700 11px system-ui, sans-serif; text-transform:uppercase; padding:4px 6px; border-radius:4px; }
|
||||
.entry.error .lvl { color:#ffffff; background:#da3633; }
|
||||
.entry.warn .lvl { color:#1f2937; background:#f59e0b; }
|
||||
.entry.info .lvl { color:#ffffff; background:#1f6feb; }
|
||||
.entry .msg { white-space:pre-wrap; word-break:break-word; font: 400 13px system-ui, sans-serif; line-height:1.5; }
|
||||
.entry .meta { color:#8b949e; font: 500 11px "SF Mono", Monaco, monospace; }
|
||||
.page-group-header { padding:12px 16px; font: 600 12px system-ui, sans-serif; color:#58a6ff; border-bottom:1px solid #21262d; background:#0d1117; }
|
||||
@media (prefers-reduced-motion:no-preference) {
|
||||
.panel { transition: all .25s cubic-bezier(0.4, 0, 0.2, 1); transform: translateY(8px) scale(0.95); opacity:0; }
|
||||
.panel.open { transform: translateY(0) scale(1); opacity:1; }
|
||||
}
|
||||
`;
|
||||
shadow.appendChild(style);
|
||||
|
||||
const btnWrap = document.createElement('div');
|
||||
btnWrap.style.position = 'relative';
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'btn';
|
||||
btn.style.opacity = String(OPACITY);
|
||||
btn.setAttribute('aria-label', 'Hugo Blox Dev HUD');
|
||||
btn.textContent = 'HB';
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'badge';
|
||||
badge.hidden = true;
|
||||
btnWrap.append(btn, badge);
|
||||
|
||||
const panel = document.createElement('div');
|
||||
panel.className = 'panel';
|
||||
|
||||
// Header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'panel-header';
|
||||
const title = document.createElement('h2');
|
||||
title.textContent = 'Hugo Blox – Dev Tools';
|
||||
const close = document.createElement('button');
|
||||
close.className = 'close-btn';
|
||||
close.textContent = '×';
|
||||
close.title = 'Close';
|
||||
header.append(title, close);
|
||||
|
||||
// Page Info Section
|
||||
const pageInfo = document.createElement('div');
|
||||
pageInfo.className = 'page-info';
|
||||
const pageTitle = document.createElement('h3');
|
||||
pageTitle.textContent = 'Current Page';
|
||||
const pageMeta = document.createElement('div');
|
||||
pageMeta.className = 'page-meta';
|
||||
pageInfo.append(pageTitle, pageMeta);
|
||||
|
||||
// Environment Info
|
||||
const envInfo = document.createElement('div');
|
||||
envInfo.className = 'env-info';
|
||||
const env = document.createElement('div');
|
||||
env.textContent = '';
|
||||
const filters = document.createElement('div');
|
||||
filters.className = 'filters';
|
||||
const chkInfo = document.createElement('input');
|
||||
chkInfo.type='checkbox';
|
||||
chkInfo.id='hbx-info';
|
||||
chkInfo.checked = !!(localStorage.getItem('hbx-show-info')||'');
|
||||
const lblInfo = document.createElement('label');
|
||||
lblInfo.htmlFor='hbx-info';
|
||||
lblInfo.textContent='Show info';
|
||||
const chkAuto = document.createElement('input');
|
||||
chkAuto.type='checkbox';
|
||||
chkAuto.id='hbx-auto-open';
|
||||
chkAuto.checked = !!(localStorage.getItem('hbx-auto-open')||'');
|
||||
const lblAuto = document.createElement('label');
|
||||
lblAuto.htmlFor='hbx-auto-open';
|
||||
lblAuto.textContent='Auto open';
|
||||
filters.append(lblInfo, chkInfo, lblAuto, chkAuto);
|
||||
envInfo.append(env, filters);
|
||||
|
||||
// Tabs
|
||||
const tabs = document.createElement('div');
|
||||
tabs.className = 'tabs';
|
||||
const logsTab = document.createElement('div');
|
||||
logsTab.className = 'tab active';
|
||||
logsTab.textContent = 'Logs';
|
||||
logsTab.dataset.tab = 'logs';
|
||||
tabs.append(logsTab);
|
||||
|
||||
// Tab Content
|
||||
const tabContent = document.createElement('div');
|
||||
tabContent.className = 'tab-content';
|
||||
const logsContent = document.createElement('div');
|
||||
logsContent.className = 'logs-content';
|
||||
tabContent.append(logsContent);
|
||||
|
||||
panel.append(header, pageInfo, envInfo, tabs, tabContent);
|
||||
|
||||
shadow.append(btnWrap, panel);
|
||||
|
||||
const attach = () => { if (!document.body.contains(root)) document.body.appendChild(root); };
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', attach);
|
||||
else attach();
|
||||
|
||||
let logs = [];
|
||||
let meta = null;
|
||||
const setLogs = (payload) => {
|
||||
if (payload) {
|
||||
logs = Array.isArray(payload.entries) ? payload.entries : [];
|
||||
meta = payload;
|
||||
} else {
|
||||
logs = [];
|
||||
meta = null;
|
||||
}
|
||||
};
|
||||
const shouldShowInfo = () => chkInfo.checked;
|
||||
const createMetaItem = (label, value, className = '') => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'meta-item';
|
||||
const labelEl = document.createElement('div');
|
||||
labelEl.className = 'meta-label';
|
||||
labelEl.textContent = label;
|
||||
const valueEl = document.createElement('div');
|
||||
valueEl.className = `meta-value ${className}`;
|
||||
valueEl.textContent = value || '(not set)';
|
||||
item.append(labelEl, valueEl);
|
||||
return item;
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
|
||||
// Update environment info
|
||||
if (meta && meta.hugo) {
|
||||
const hv = meta.hugo.version || '';
|
||||
const gvRaw = meta.hugo.goVersion || '';
|
||||
const gv = gvRaw.replace(/^go/i, '');
|
||||
const tw = meta.modules && meta.modules.bloxTailwind ? meta.modules.bloxTailwind.version : '';
|
||||
env.textContent = `Hugo ${hv} · Go ${gv}${tw ? ` · blox-tailwind ${tw}` : ''}`;
|
||||
}
|
||||
|
||||
// Update page information
|
||||
pageMeta.replaceChildren();
|
||||
if (meta && meta.currentPage) {
|
||||
const page = meta.currentPage;
|
||||
pageMeta.append(
|
||||
createMetaItem('Type', page.type || 'unknown', 'type'),
|
||||
createMetaItem('Layout', page.layout || '(none)', 'layout'),
|
||||
createMetaItem('Kind', page.kind || 'unknown'),
|
||||
createMetaItem('Section', page.section || '(none)'),
|
||||
createMetaItem('Path', page.path || window.location.pathname),
|
||||
createMetaItem('File', page.file || '(virtual page)'),
|
||||
createMetaItem('Build ID', page.buildId || 'unknown')
|
||||
);
|
||||
} else {
|
||||
// Show placeholder data when meta isn't loaded yet
|
||||
pageMeta.append(
|
||||
createMetaItem('Type', 'loading...', 'type'),
|
||||
createMetaItem('Layout', 'loading...', 'layout'),
|
||||
createMetaItem('Kind', 'loading...'),
|
||||
createMetaItem('Section', 'loading...'),
|
||||
createMetaItem('Path', window.location.pathname),
|
||||
createMetaItem('File', 'loading...'),
|
||||
createMetaItem('Build ID', 'loading...')
|
||||
);
|
||||
}
|
||||
|
||||
// Update logs
|
||||
const visible = logs.filter(e => e.level==='error' || e.level==='warn' || (e.level==='info' && shouldShowInfo()));
|
||||
const errs = visible.filter(e => e.level==='error').length;
|
||||
const warns = visible.filter(e => e.level==='warn').length;
|
||||
const total = errs + warns + (shouldShowInfo()? visible.filter(e=>e.level==='info').length : 0);
|
||||
badge.textContent = String(total);
|
||||
badge.hidden = total === 0;
|
||||
|
||||
// Update tab badge
|
||||
logsTab.textContent = `Logs${total > 0 ? ` (${total})` : ''}`;
|
||||
|
||||
logsContent.replaceChildren();
|
||||
const groupByPage = new Map();
|
||||
for (const e of visible) {
|
||||
const key = e.page || '/';
|
||||
if (!groupByPage.has(key)) groupByPage.set(key, []);
|
||||
groupByPage.get(key).push(e);
|
||||
}
|
||||
for (const [page, entries] of groupByPage) {
|
||||
const h = document.createElement('div');
|
||||
h.className = 'page-group-header';
|
||||
h.textContent = page;
|
||||
logsContent.appendChild(h);
|
||||
for (const e of entries.sort((a,b)=> (a.level>b.level?-1:1) || (a.timestamp-b.timestamp))) {
|
||||
const row = document.createElement('div');
|
||||
row.className = `entry ${e.level}`;
|
||||
const lvl = document.createElement('div');
|
||||
lvl.className='lvl';
|
||||
lvl.textContent=e.level;
|
||||
const msg = document.createElement('div');
|
||||
msg.className='msg';
|
||||
msg.textContent = e.message || '';
|
||||
const metaEl = document.createElement('div');
|
||||
metaEl.className='meta';
|
||||
metaEl.textContent = new Date(e.timestamp*1000).toLocaleTimeString();
|
||||
row.append(lvl, msg, metaEl);
|
||||
logsContent.appendChild(row);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const refreshFromWindow = () => {
|
||||
if (window.__HBX_LOGS__) {
|
||||
setLogs(window.__HBX_LOGS__);
|
||||
}
|
||||
};
|
||||
const fetchLogs = async () => {
|
||||
try {
|
||||
const res = await fetch('/hbx-debug.json?cb=' + Date.now());
|
||||
if (res.ok) { const data = await res.json(); setLogs(data); render(); }
|
||||
} catch (e) { /* silent */ }
|
||||
};
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
panel.classList.toggle('open');
|
||||
if (panel.classList.contains('open')) {
|
||||
refreshFromWindow();
|
||||
render(); // Immediately render current data
|
||||
fetchLogs();
|
||||
}
|
||||
});
|
||||
close.addEventListener('click', () => panel.classList.remove('open'));
|
||||
chkInfo.addEventListener('change', () => {
|
||||
localStorage.setItem('hbx-show-info', chkInfo.checked ? '1' : '');
|
||||
render();
|
||||
});
|
||||
chkAuto.addEventListener('change', () => {
|
||||
localStorage.setItem('hbx-auto-open', chkAuto.checked ? '1' : '');
|
||||
});
|
||||
|
||||
// Initial render immediately on load
|
||||
refreshFromWindow();
|
||||
render(); // Force initial render even without data
|
||||
|
||||
// Auto-open panel if user enabled it
|
||||
if (chkAuto.checked) {
|
||||
panel.classList.add('open');
|
||||
render();
|
||||
fetchLogs();
|
||||
}
|
||||
|
||||
// Auto-refresh badge every few seconds in dev
|
||||
setInterval(() => { refreshFromWindow(); }, 3000);
|
||||
} catch(e) { /* ignore */ }
|
||||
})();
|
||||
</script>
|
||||
{{- end -}}
|
||||
@@ -25,7 +25,7 @@
|
||||
{{ if $client_jsx }}
|
||||
{{/* Build the client-side JSX using Hugo's official integration */}}
|
||||
{{ $jsx_opts := dict
|
||||
"targetPath" (printf "js/%s-preact.js" $block_type)
|
||||
"targetPath" (printf "assets/js/preact-built/%s.js" $block_type)
|
||||
"JSX" "automatic"
|
||||
"JSXImportSource" "preact"
|
||||
"format" "iife"
|
||||
|
||||
@@ -2,50 +2,64 @@
|
||||
{{/* Documentation: https://hugoblox.com/docs/ */}}
|
||||
{{/* License: https://github.com/HugoBlox/hugo-blox-builder/blob/main/LICENSE.md */}}
|
||||
|
||||
{{/*
|
||||
{{/*
|
||||
This partial generates Tailwind CSS @source directives that tell Tailwind where to scan for CSS classes.
|
||||
|
||||
CRITICAL: Tailwind scans the actual filesystem (relative to main.css), while Hugo operates on a virtual
|
||||
filesystem with module mounts. This file bridges that gap by translating Hugo's virtual paths to actual
|
||||
filesystem paths that Tailwind can find.
|
||||
|
||||
Environments handled:
|
||||
1. Test site (HUGO_BLOX_TEST_SITE=true): runs from test/, so ../modules/blox-tailwind/ for theme files
|
||||
2. Starter sites (HUGO_BLOX_MONOREPO=true): run from starters/[name]/, so ../../../modules/blox-tailwind/
|
||||
3. End-user sites: use virtual mounted paths from Hugo modules (standard content/, layouts/, assets/)
|
||||
|
||||
Without this, Tailwind would miss classes from:
|
||||
* Hugo Blox theme templates (layouts/, blox/)
|
||||
* Preact components (blox/** /*.jsx) - intentional space due to Hugo bug thinking '* /' is commend end
|
||||
* Site content (content/** /*.md) - intentional space due to Hugo bug thinking '* /' is commend end
|
||||
* Generated HTML classes (hugo_stats.json)
|
||||
It provides a robust bridge between Hugo's virtual filesystem (which includes module mounts)
|
||||
and Tailwind's real filesystem scanner.
|
||||
*/}}
|
||||
{{ $is_monorepo := eq (os.Getenv "HUGO_BLOX_MONOREPO") "true" }}
|
||||
{{ $is_test_site := eq (os.Getenv "HUGO_BLOX_TEST_SITE") "true" }}
|
||||
{{ $paths := slice }}
|
||||
|
||||
{{/* 1. Always add paths relative to the current site's root (content, layouts, etc.) */}}
|
||||
{{/* 1. Always scan the site's source files. */}}
|
||||
{{ $paths = $paths | append `content/**/*.md` }}
|
||||
{{ $paths = $paths | append `layouts/**/*.html` }}
|
||||
{{ $paths = $paths | append `assets/js/**/*.js` }}
|
||||
{{ $paths = $paths | append `assets/js/**/*.jsx` }}
|
||||
{{ $paths = $paths | append `assets/js/**/*.tsx` }}
|
||||
{{ $paths = $paths | append `hugo_stats.json` }}
|
||||
|
||||
{{/* 2. If in monorepo, add relative paths to the blox-tailwind module source */}}
|
||||
{{ if $is_monorepo }}
|
||||
{{ $module_path_prefix := "" }}
|
||||
{{ if $is_test_site }}
|
||||
{{/* Test site is at `test/`, so one level up to repo root */}}
|
||||
{{ $module_path_prefix = "../" }}
|
||||
{{ else }}
|
||||
{{/* Starter sites are at `starters/[name]/`, so three levels up */}}
|
||||
{{ $module_path_prefix = "../../../" }}
|
||||
{{/*
|
||||
2. Crucially, scan Hugo's generated JS output from Preact components.
|
||||
These files are built by Hugo's `js.Build` into a predictable location within the
|
||||
project's `assets` directory, making them a reliable source for Tailwind to scan.
|
||||
This step happens before Tailwind runs, thanks to `templates.Defer` in `site_head.html`.
|
||||
*/}}
|
||||
{{ $paths = $paths | append `assets/js/preact-built/**/*.js` }}
|
||||
|
||||
{{/*
|
||||
3. Inline sources: extract class attributes from Preact JSX/TSX and embed them
|
||||
directly so Tailwind can always see them regardless of FS layout.
|
||||
*/}}
|
||||
{{ $jsx_files := resources.Match "js/hbx/**/*.jsx" }}
|
||||
{{ $tsx_files := resources.Match "js/hbx/**/*.tsx" }}
|
||||
|
||||
{{ $extracted_classes := slice }}
|
||||
{{ range $f := $jsx_files }}
|
||||
{{ $code := $f.Content }}
|
||||
{{ $matches := findRE `class(?:Name)?\s*=\s*"([^"]+)"` $code }}
|
||||
{{ range $m := $matches }}
|
||||
{{ $cls := replaceRE `^class(?:Name)?\s*=\s*"([^"]+)"$` `$1` $m }}
|
||||
{{ $classes := split $cls " " }}
|
||||
{{ range $class := $classes }}
|
||||
{{ $extracted_classes = $extracted_classes | append $class }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $module_path := printf "%smodules/blox-tailwind" $module_path_prefix }}
|
||||
{{ $paths = $paths | append (printf `%s/layouts/**/*.html` $module_path) }}
|
||||
{{ $paths = $paths | append (printf `%s/assets/js/**/*.js` $module_path) }}
|
||||
{{ $paths = $paths | append (printf `%s/blox/**/*.html` $module_path) }}
|
||||
{{ $paths = $paths | append (printf `%s/blox/**/*.jsx` $module_path) }}
|
||||
{{ end }}
|
||||
{{ range $f := $tsx_files }}
|
||||
{{ $code := $f.Content }}
|
||||
{{ $matches := findRE `class(?:Name)?\s*=\s*"([^"]+)"` $code }}
|
||||
{{ range $m := $matches }}
|
||||
{{ $cls := replaceRE `^class(?:Name)?\s*=\s*"([^"]+)"$` `$1` $m }}
|
||||
{{ $classes := split $cls " " }}
|
||||
{{ range $class := $classes }}
|
||||
{{ $extracted_classes = $extracted_classes | append $class }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $extracted_classes = $extracted_classes | uniq }}
|
||||
{{ range $class := $extracted_classes }}
|
||||
@source inline("{{ $class }}");
|
||||
{{ end }}
|
||||
|
||||
{{/* Generate the @source rules */}}
|
||||
|
||||
@@ -23,7 +23,19 @@
|
||||
*/}}
|
||||
|
||||
{{/* Log deprecation warning */}}
|
||||
{{ partial "functions/logger" (dict "page" .Page "level" "warn" "message" "The callout shortcode is deprecated. Use standard Markdown alert syntax instead: > [!NOTE]") }}
|
||||
{{ $src := "/" }}
|
||||
{{ with .Page }}
|
||||
{{ with .File }}
|
||||
{{ $src = .Path }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ partial "functions/logger" (dict
|
||||
"page" .Page
|
||||
"level" "warn"
|
||||
"id" "callout-deprecated"
|
||||
"message" (printf "The `callout` shortcode is deprecated in %s. Use standard Markdown alert syntax instead, such as `> [!NOTE]`." $src)
|
||||
"source" (dict "type" "shortcode" "name" "callout" "docs" "https://docs.hugoblox.com/reference/markdown/#callouts")
|
||||
) }}
|
||||
|
||||
{{/* Get callout type from parameter */}}
|
||||
{{ $type := (.Get 0) | default "note" }}
|
||||
|
||||
@@ -33,5 +33,9 @@
|
||||
|
||||
{{/* EXTENSIBILITY HOOK: BODY-END */}}
|
||||
{{ partial "functions/get_hook" (dict "hook" "body-end" "context" .) }}
|
||||
|
||||
{{/* HBX Dev: ensure debug export and HUD are injected in dev */}}
|
||||
{{ partial "hooks/body-end/hbx-debug-export.html" . }}
|
||||
{{ partial "hooks/body-end/hbx-debug-hud.html" . }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,80 +0,0 @@
|
||||
{{/*
|
||||
Hugo Blox Builder Shared Callout Renderer
|
||||
|
||||
Renders callouts with consistent styling for both shortcodes and render hooks.
|
||||
|
||||
Parameters:
|
||||
- type: The callout type (note, tip, important, warning, caution, or custom icon name)
|
||||
- content: The callout content (HTML)
|
||||
- title: Optional title for the callout
|
||||
- source: "shortcode" or "renderhook" for context
|
||||
*/}}
|
||||
|
||||
{{ $type := (lower .type) | default "note" }}
|
||||
{{ $content := .content }}
|
||||
{{ $title := .title }}
|
||||
{{ if not $title }}
|
||||
{{/* Try i18n translation with "callout_" prefix, fall back to title case */}}
|
||||
{{ $i18n_key := printf "callout_%s" $type }}
|
||||
{{ $title = or (i18n $i18n_key) (title $type) }}
|
||||
{{ end }}
|
||||
{{ $source := .source | default "shortcode" }}
|
||||
|
||||
{{/* Define icon and styling mappings for different alert types */}}
|
||||
{{ $alertConfigs := dict
|
||||
"note" (dict
|
||||
"icon" "information-circle"
|
||||
"class" "bg-primary-100 dark:bg-primary-900"
|
||||
"textClass" "text-primary-600 dark:text-primary-300"
|
||||
)
|
||||
"tip" (dict
|
||||
"icon" "light-bulb"
|
||||
"class" "bg-green-100 dark:bg-green-900"
|
||||
"textClass" "text-green-600 dark:text-green-300"
|
||||
)
|
||||
"important" (dict
|
||||
"icon" "exclamation-circle"
|
||||
"class" "bg-purple-100 dark:bg-purple-900"
|
||||
"textClass" "text-purple-600 dark:text-purple-300"
|
||||
)
|
||||
"warning" (dict
|
||||
"icon" "exclamation-triangle"
|
||||
"class" "bg-yellow-100 dark:bg-yellow-900"
|
||||
"textClass" "text-red-400"
|
||||
)
|
||||
"caution" (dict
|
||||
"icon" "exclamation-triangle"
|
||||
"class" "bg-red-100 dark:bg-red-900"
|
||||
"textClass" "text-red-600 dark:text-red-300"
|
||||
)
|
||||
}}
|
||||
|
||||
{{/* Get configuration for this callout type, or use custom icon */}}
|
||||
{{ $config := index $alertConfigs $type }}
|
||||
{{ $icon := $type }}
|
||||
{{ $class := "bg-primary-100 dark:bg-primary-900" }}
|
||||
{{ $textClass := "text-primary-600 dark:text-primary-300" }}
|
||||
|
||||
{{ if $config }}
|
||||
{{ $icon = $config.icon }}
|
||||
{{ $class = $config.class }}
|
||||
{{ $textClass = $config.textClass }}
|
||||
{{ end }}
|
||||
|
||||
<div class="flex px-4 py-3 mb-6 rounded-md {{ $class }}">
|
||||
<span class="pr-3 pt-1 {{ $textClass }}">
|
||||
{{ partial "functions/get_icon" (dict "name" $icon "attributes" "height=\"24\"") }}
|
||||
</span>
|
||||
<div class="dark:text-neutral-300">
|
||||
{{/* Display title if provided */}}
|
||||
{{- with $title -}}
|
||||
<div class="font-semibold mb-1">
|
||||
{{- . -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{/* Display content */}}
|
||||
<div>
|
||||
{{- $content -}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,6 +106,7 @@ HUGO_ARGS=(
|
||||
|
||||
if [ "$DEBUG_MODE" = true ]; then
|
||||
HUGO_ARGS+=(
|
||||
--panicOnWarning
|
||||
--logLevel debug
|
||||
--templateMetrics
|
||||
--templateMetricsHints
|
||||
|
||||
@@ -84,6 +84,7 @@ HUGO_ARGS=(
|
||||
|
||||
if [ "$DEBUG_MODE" = true ]; then
|
||||
HUGO_ARGS+=(
|
||||
--panicOnWarning
|
||||
--logLevel debug
|
||||
--templateMetrics
|
||||
--templateMetricsHints
|
||||
|
||||
@@ -22,6 +22,6 @@ main:
|
||||
- name: Projects
|
||||
url: projects/
|
||||
weight: 30
|
||||
- name: Teaching
|
||||
- name: Courses
|
||||
url: courses/
|
||||
weight: 40
|
||||
@@ -118,6 +118,6 @@ sections:
|
||||
design:
|
||||
card:
|
||||
# Card background color (CSS class)
|
||||
css_class: 'bg-primary-700'
|
||||
css_class: 'bg-primary-300'
|
||||
css_style: ''
|
||||
---
|
||||
@@ -115,24 +115,21 @@ renders as
|
||||
|
||||
Use [callouts](https://docs.hugoblox.com/reference/markdown/#callouts) (aka _asides_, _hints_, or _alerts_) to draw attention to notes, tips, and warnings.
|
||||
|
||||
By wrapping a paragraph in `{{%/* callout note */%}} ... {{%/* /callout */%}}`, it will render as an aside.
|
||||
Use the `> [!NOTE]` syntax to create a callout.
|
||||
|
||||
```markdown
|
||||
{{%/* callout note */%}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{%/* /callout */%}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{% callout note %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
Or use the `warning` callout type so your readers don't miss critical details:
|
||||
|
||||
{{% callout warning %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!WARNING]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
## Did you find this page helpful? Consider sharing it 🙌
|
||||
@@ -4,8 +4,9 @@ summary: My courses
|
||||
type: landing
|
||||
|
||||
cascade:
|
||||
- _target:
|
||||
kind: page
|
||||
- target:
|
||||
path: '{/courses/*/**}'
|
||||
type: docs
|
||||
params:
|
||||
show_breadcrumb: true
|
||||
|
||||
@@ -15,9 +16,13 @@ sections:
|
||||
content:
|
||||
title: Courses
|
||||
filters:
|
||||
folders:
|
||||
- courses
|
||||
tag: Course
|
||||
kinds:
|
||||
- section
|
||||
design:
|
||||
view: article-grid
|
||||
columns: 2
|
||||
show_read_time: false
|
||||
show_date: false
|
||||
show_read_more: false
|
||||
columns: 1
|
||||
---
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
linkTitle: Hugo Blox
|
||||
title: Learn Hugo Blox in 10 minutes!
|
||||
date: 2025-09-24
|
||||
type: docs
|
||||
|
||||
tags:
|
||||
- Hugo Blox
|
||||
- Course
|
||||
|
||||
content_meta:
|
||||
content_type: 'Course'
|
||||
difficulty: 'Beginner'
|
||||
prerequisites: ['Markdown']
|
||||
trending: false
|
||||
---
|
||||
|
||||
👋 Welcome to your shiny new site. Ready to learn more?
|
||||
|
||||
<!--more-->
|
||||
|
||||
This course provides a brief introduction to creating and editing your site with Hugo Blox. For full documentation, refer to the [Hugo Blox Documentation](https://docs.hugoblox.com/).
|
||||
|
||||
## Next
|
||||
|
||||
{{< cards >}}
|
||||
{{< card url="getting-started" title="Get Started" icon="document-text" subtitle="Setup your new site in just 5 minutes!" >}}
|
||||
{{< /cards >}}
|
||||
File renamed without changes.
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: Getting Started
|
||||
date: 2024-02-17
|
||||
weight: 1
|
||||
type: docs
|
||||
---
|
||||
|
||||
## Quick Start from Template
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Create a site
|
||||
|
||||
[Click to choose your starting template](https://hugoblox.com/templates)
|
||||
|
||||
### Configure your new site
|
||||
|
||||
[Configure your site name, description, and menu.](https://docs.hugoblox.com/tutorial/blog/)
|
||||
|
||||
### Add your content
|
||||
|
||||
[Edit the homepage and add your documentation pages.](https://docs.hugoblox.com/tutorial/blog/)
|
||||
|
||||
### Publish your site
|
||||
|
||||
Your site will automatically publish ~1-5 minutes after you commit (save) changes to files in your GitHub repository.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## Next
|
||||
|
||||
Let's customize your new site:
|
||||
|
||||
{{< cards >}}
|
||||
{{< card url="../guide/project-structure" title="Project Structure" icon="document-duplicate" >}}
|
||||
{{< card url="../guide/configuration" title="Configuration" icon="adjustments-vertical" >}}
|
||||
{{< card url="../guide/formatting" title="Create content" icon="document-duplicate" >}}
|
||||
{{< /cards >}}
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Guide
|
||||
weight: 2
|
||||
sidebar:
|
||||
open: true
|
||||
---
|
||||
|
||||
{{< cards >}}
|
||||
{{< card url="formatting" title="Formatting" icon="document-duplicate" >}}
|
||||
{{< card url="project-structure" title="Project Structure" icon="document-duplicate" >}}
|
||||
{{< card url="configuration" title="Configuration" icon="adjustments-vertical" >}}
|
||||
{{< /cards >}}
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Configuration
|
||||
weight: 2
|
||||
---
|
||||
|
||||
The configuration of your site can be found in `config/_default/`.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Full Documentation
|
||||
|
||||
See https://docs.hugoblox.com/getting-started/customize/
|
||||
|
||||
## Navigation
|
||||
|
||||
### Menu
|
||||
|
||||
See https://docs.hugoblox.com/getting-started/customize/#menu-items
|
||||
|
||||
## Left Sidebar
|
||||
|
||||
Links are automatically generated from the structure of your content directory. Simply add a folder to nest a page.
|
||||
|
||||
### Extra Links
|
||||
|
||||
Additional links can be added under the `sidebar` section of your `config/_default/menus.yaml`:
|
||||
|
||||
```yaml
|
||||
menu:
|
||||
sidebar:
|
||||
- name: "Need help?"
|
||||
params:
|
||||
type: separator
|
||||
weight: 1
|
||||
- name: "A page"
|
||||
pageRef: "/page-filename-here"
|
||||
weight: 2
|
||||
- name: "An external link ↗"
|
||||
url: "https://hugoblox.com"
|
||||
weight: 3
|
||||
```
|
||||
|
||||
## Right Sidebar
|
||||
|
||||
A table of contents is automatically generated from the headings your Markdown file.
|
||||
|
||||
It can optionally be disabled by setting `toc: false` in the front matter of a page:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Page
|
||||
toc: false
|
||||
---
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Formatting
|
||||
---
|
||||
|
||||
Write rich, engaging content with Markdown and re-usable Hugo Blox components (shortcodes):
|
||||
|
||||
{{< cards >}}
|
||||
{{< card url="button" title="Button" icon="cursor-arrow-rays" >}}
|
||||
{{< card url="callout" title="Callout" icon="warning" >}}
|
||||
{{< card url="cards" title="Cards" icon="card" >}}
|
||||
{{< card url="toggle" title="Spoiler" icon="chevron-right" >}}
|
||||
{{< card url="steps" title="Steps" icon="one" >}}
|
||||
{{< /cards >}}
|
||||
File renamed without changes.
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Buttons
|
||||
linkTitle: Buttons
|
||||
---
|
||||
|
||||
A modern, customizable button shortcode with gradient styling, icons, and smart link handling.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Basic Usage
|
||||
|
||||
{{< button url="/contact" >}}Contact Us{{< /button >}}
|
||||
|
||||
{{< button url="https://example.com" new_tab="true" style="secondary" >}}Visit External Site{{< /button >}}
|
||||
|
||||
The above buttons are created with:
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="/contact" */>}}Contact Us{{</* /button */>}}
|
||||
|
||||
{{</* button url="https://example.com" new_tab="true" style="secondary" */>}}Visit External Site{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Style Variants
|
||||
|
||||
### Primary (Default)
|
||||
|
||||
{{< button url="#" style="primary" >}}Primary Button{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" style="primary" */>}}Primary Button{{</* /button */>}}
|
||||
```
|
||||
|
||||
### Secondary
|
||||
|
||||
{{< button url="#" style="secondary" >}}Secondary Button{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" style="secondary" */>}}Secondary Button{{</* /button */>}}
|
||||
```
|
||||
|
||||
### Outline
|
||||
|
||||
{{< button url="#" style="outline" >}}Outline Button{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" style="outline" */>}}Outline Button{{</* /button */>}}
|
||||
```
|
||||
|
||||
### Ghost
|
||||
|
||||
{{< button url="#" style="ghost" >}}Ghost Button{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" style="ghost" */>}}Ghost Button{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
### Small
|
||||
|
||||
{{< button url="#" size="sm" >}}Small Button{{< /button >}}
|
||||
|
||||
### Medium (Default)
|
||||
|
||||
{{< button url="#" size="md" >}}Medium Button{{< /button >}}
|
||||
|
||||
### Large
|
||||
|
||||
{{< button url="#" size="lg" >}}Large Button{{< /button >}}
|
||||
|
||||
### Extra Large
|
||||
|
||||
{{< button url="#" size="xl" >}}Extra Large{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" size="sm" */>}}Small Button{{</* /button */>}}
|
||||
{{</* button url="#" size="md" */>}}Medium Button{{</* /button */>}}
|
||||
{{</* button url="#" size="lg" */>}}Large Button{{</* /button */>}}
|
||||
{{</* button url="#" size="xl" */>}}Extra Large{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Alignment
|
||||
|
||||
### Left (Default)
|
||||
|
||||
{{< button url="#" align="left" >}}Left Aligned{{< /button >}}
|
||||
|
||||
### Center
|
||||
|
||||
{{< button url="#" align="center" >}}Center Aligned{{< /button >}}
|
||||
|
||||
### Right
|
||||
|
||||
{{< button url="#" align="right" >}}Right Aligned{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" align="left" */>}}Left Aligned{{</* /button */>}}
|
||||
{{</* button url="#" align="center" */>}}Center Aligned{{</* /button */>}}
|
||||
{{</* button url="#" align="right" */>}}Right Aligned{{</* /button */>}}
|
||||
```
|
||||
|
||||
## With Icons
|
||||
|
||||
### Icon Before Text
|
||||
|
||||
{{< button url="#" icon="arrow-down-tray" >}}Download{{< /button >}}
|
||||
|
||||
### Icon After Text
|
||||
|
||||
{{< button url="#" icon="arrow-right" icon_position="right" >}}Continue{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" icon="arrow-down-tray" */>}}Download{{</* /button */>}}
|
||||
{{</* button url="#" icon="arrow-right" icon_position="right" */>}}Continue{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Rounded Corners
|
||||
|
||||
### Small Radius
|
||||
|
||||
{{< button url="#" rounded="sm" >}}Small Radius{{< /button >}}
|
||||
|
||||
### Medium Radius (Default)
|
||||
|
||||
{{< button url="#" rounded="md" >}}Medium Radius{{< /button >}}
|
||||
|
||||
### Large Radius
|
||||
|
||||
{{< button url="#" rounded="lg" >}}Large Radius{{< /button >}}
|
||||
|
||||
### Pill Shape
|
||||
|
||||
{{< button url="#" rounded="full" >}}Pill Button{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="#" rounded="sm" */>}}Small Radius{{</* /button */>}}
|
||||
{{</* button url="#" rounded="md" */>}}Medium Radius{{</* /button */>}}
|
||||
{{</* button url="#" rounded="lg" */>}}Large Radius{{</* /button */>}}
|
||||
{{</* button url="#" rounded="full" */>}}Pill Button{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Advanced Examples
|
||||
|
||||
### Call-to-Action Button
|
||||
|
||||
{{< button url="/signup" style="primary" size="lg" align="center" icon="rocket-launch" >}}Get Started Today{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="/signup" style="primary" size="lg" align="center" icon="rocket-launch" */>}}Get Started Today{{</* /button */>}}
|
||||
```
|
||||
|
||||
### External Link with New Tab
|
||||
|
||||
{{< button url="https://github.com/hugo-blox/hugo-blox-builder" new_tab="true" style="outline" icon="arrow-top-right-on-square" icon_position="right" >}}View on GitHub{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="https://github.com/hugo-blox/hugo-blox-builder" new_tab="true" style="outline" icon="arrow-top-right-on-square" icon_position="right" */>}}View on GitHub{{</* /button */>}}
|
||||
```
|
||||
|
||||
### Download Button
|
||||
|
||||
{{< button url="/files/document.pdf" style="secondary" icon="document-arrow-down" rounded="full" >}}Download PDF{{< /button >}}
|
||||
|
||||
```go-html-template
|
||||
{{</* button url="/files/document.pdf" style="secondary" icon="document-arrow-down" rounded="full" */>}}Download PDF{{</* /button */>}}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| --------------- | ------- | ------------- | ----------------------------------------------------------- |
|
||||
| `url` | string | `#` | **Required.** Button destination URL (internal or external) |
|
||||
| `text` | string | Inner content | Button text (overrides shortcode content) |
|
||||
| `new_tab` | boolean | `false` | Whether to open link in new tab |
|
||||
| `style` | string | `primary` | Button style: `primary`, `secondary`, `outline`, `ghost` |
|
||||
| `size` | string | `md` | Button size: `sm`, `md`, `lg`, `xl` |
|
||||
| `align` | string | `left` | Button alignment: `left`, `center`, `right` |
|
||||
| `icon` | string | - | Icon name from [Hero Icons](https://heroicons.com/) |
|
||||
| `icon_position` | string | `left` | Icon position: `left`, `right` |
|
||||
| `rounded` | string | `md` | Border radius: `sm`, `md`, `lg`, `xl`, `full` |
|
||||
| `disabled` | boolean | `false` | Whether button should be disabled |
|
||||
|
||||
## Security Features
|
||||
|
||||
The button shortcode automatically handles security for external links:
|
||||
|
||||
- **External links** get `rel="noreferrer"` attribute
|
||||
- **External links opening in new tab** get `rel="noopener noreferrer"`
|
||||
- **Internal links opening in new tab** get `rel="noopener"`
|
||||
|
||||
This ensures safe navigation while maintaining functionality.
|
||||
|
||||
## Accessibility
|
||||
|
||||
The button shortcode includes built-in accessibility features:
|
||||
|
||||
- Proper `role="button"` attribute
|
||||
- `aria-label` support
|
||||
- Keyboard focus indicators
|
||||
- High contrast focus rings
|
||||
- Disabled state handling
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: Callouts
|
||||
linkTitle: Callouts
|
||||
---
|
||||
|
||||
Hugo Blox supports GitHub and Obsidian-style Markdown callouts for maximum compatibility and content portability.
|
||||
|
||||
Callouts are a useful feature to draw attention to important or related content such as notes, hints, or warnings in your articles.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Callout Types
|
||||
|
||||
Hugo Blox supports all 15+ callout types from [Obsidian's documentation](https://help.obsidian.md/callouts):
|
||||
|
||||
**Information & Notes:**
|
||||
|
||||
```markdown
|
||||
> [!NOTE]
|
||||
> This is a note callout with important information that users should know.
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is a note callout with important information that users should know.
|
||||
|
||||
```markdown
|
||||
> [!INFO]
|
||||
> Alternative info callout - same styling as NOTE.
|
||||
```
|
||||
|
||||
> [!INFO]
|
||||
> Alternative info callout - same styling as NOTE.
|
||||
|
||||
```markdown
|
||||
> [!ABSTRACT]
|
||||
> Use for abstracts, summaries, or TL;DR sections.
|
||||
```
|
||||
|
||||
> [!ABSTRACT]
|
||||
> Use for abstracts, summaries, or TL;DR sections.
|
||||
|
||||
**Actions & Tasks:**
|
||||
|
||||
```markdown
|
||||
> [!TODO]
|
||||
> This is something that needs to be done.
|
||||
```
|
||||
|
||||
> [!TODO]
|
||||
> This is something that needs to be done.
|
||||
|
||||
```markdown
|
||||
> [!TIP]
|
||||
> Here's a helpful tip to make your workflow more efficient!
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Here's a helpful tip to make your workflow more efficient!
|
||||
|
||||
```markdown
|
||||
> [!SUCCESS]
|
||||
> Great job! This operation completed successfully.
|
||||
```
|
||||
|
||||
> [!SUCCESS]
|
||||
> Great job! This operation completed successfully.
|
||||
|
||||
**Questions & Interactive:**
|
||||
|
||||
```markdown
|
||||
> [!QUESTION]
|
||||
> What do you think about this approach? Let us know!
|
||||
```
|
||||
|
||||
> [!QUESTION]
|
||||
> What do you think about this approach? Let us know!
|
||||
|
||||
```markdown
|
||||
> [!EXAMPLE]
|
||||
> Here's a practical example of how to implement this feature.
|
||||
```
|
||||
|
||||
> [!EXAMPLE]
|
||||
> Here's a practical example of how to implement this feature.
|
||||
|
||||
```markdown
|
||||
> [!QUOTE]
|
||||
> "The best way to predict the future is to invent it." - Alan Kay
|
||||
```
|
||||
|
||||
> [!QUOTE]
|
||||
> "The best way to predict the future is to invent it." - Alan Kay
|
||||
|
||||
**Warnings & Errors:**
|
||||
|
||||
```markdown
|
||||
> [!WARNING]
|
||||
> Be careful! This action might have unexpected consequences.
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Be careful! This action might have unexpected consequences.
|
||||
|
||||
```markdown
|
||||
> [!CAUTION]
|
||||
> Danger! This operation is irreversible and could cause data loss.
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Danger! This operation is irreversible and could cause data loss.
|
||||
|
||||
```markdown
|
||||
> [!IMPORTANT]
|
||||
> This is critical information that users must understand to proceed.
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This is critical information that users must understand to proceed.
|
||||
|
||||
```markdown
|
||||
> [!DANGER]
|
||||
> This is extremely dangerous - proceed with extreme caution!
|
||||
```
|
||||
|
||||
> [!DANGER]
|
||||
> This is extremely dangerous - proceed with extreme caution!
|
||||
|
||||
```markdown
|
||||
> [!FAILURE]
|
||||
> This operation failed. Check your configuration and try again.
|
||||
```
|
||||
|
||||
> [!FAILURE]
|
||||
> This operation failed. Check your configuration and try again.
|
||||
|
||||
```markdown
|
||||
> [!BUG]
|
||||
> Known issue: This feature doesn't work properly in Safari < 14.
|
||||
```
|
||||
|
||||
> [!BUG]
|
||||
> Known issue: This feature doesn't work properly in Safari < 14.
|
||||
|
||||
### Custom Titles
|
||||
|
||||
You can customize the title of any callout:
|
||||
|
||||
```markdown
|
||||
> [!WARNING]+ Custom Warning Title
|
||||
> This warning has a custom title instead of just "Warning".
|
||||
```
|
||||
|
||||
> [!WARNING]+ Custom Warning Title
|
||||
> This warning has a custom title instead of just "Warning".
|
||||
|
||||
### Legacy Syntax (Deprecated)
|
||||
|
||||
The old shortcode syntax still works but shows a deprecation warning:
|
||||
|
||||
```markdown
|
||||
{{</* callout note */>}}
|
||||
This still works but is deprecated. Use the Markdown syntax above!
|
||||
{{</* /callout */>}}
|
||||
```
|
||||
|
||||
### CSS Customization
|
||||
|
||||
Hugo Blox generates callouts with semantic CSS classes and data attributes, making customization easy. Each callout has:
|
||||
|
||||
- Base class: `.callout`
|
||||
- Type-specific data attribute: `data-callout="note"`
|
||||
- Component classes: `.callout-icon`, `.callout-title`, `.callout-content`, `.callout-body`
|
||||
|
||||
**Custom CSS Example** (add to your `assets/css/custom.css`):
|
||||
|
||||
```css
|
||||
/* Customize NOTE callouts */
|
||||
.callout[data-callout="note"] {
|
||||
border-left-width: 8px;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
/* Make SUCCESS callouts pulse */
|
||||
.callout[data-callout="success"] {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Custom icon styling */
|
||||
.callout-icon svg {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.callout:hover .callout-icon svg {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Dark mode overrides */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.callout[data-callout="warning"] {
|
||||
background-color: rgb(92 25 2);
|
||||
border-color: rgb(245 158 11);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This approach matches [Obsidian's CSS customization system](https://help.obsidian.md/callouts), ensuring your styles work across platforms.
|
||||
|
||||
### Benefits of the New Syntax
|
||||
|
||||
- **Portable**: Works with GitHub, Obsidian, and other Markdown editors
|
||||
- **Standard**: Uses widely-adopted Markdown callout syntax
|
||||
- **Clean**: No Hugo-specific shortcodes needed
|
||||
- **Future-proof**: Supported by the latest Hugo versions (0.132.0+)
|
||||
- **Customizable**: Semantic CSS classes and data attributes for easy styling
|
||||
- **Complete**: All 15+ Obsidian callout types supported
|
||||
- **Multilingual**: Callout titles are automatically translated based on your site's language (and can be customized in the language packs)
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Cards
|
||||
linkTitle: Cards
|
||||
---
|
||||
|
||||
A Hugo extension to create cards. Cards can be shown as links or as plain text.
|
||||
|
||||
## Usage
|
||||
|
||||
{{< cards >}}
|
||||
{{< card url="../" title="Learn Shortcodes" icon="academic-cap" >}}
|
||||
{{< card url="" title="A card without an icon or link" >}}
|
||||
{{< /cards >}}
|
||||
|
||||
is rendered by:
|
||||
|
||||
```
|
||||
{{</* cards */>}}
|
||||
{{</* card url="../callout" title="Callout" icon="academic-cap" */>}}
|
||||
{{</* card url="" title="A card without an icon" */>}}
|
||||
{{</* /cards */>}}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Parameter | Description |
|
||||
|------------|------------------------------------------------------------------------|
|
||||
| `icon` | Name of the icon. Defaults to [Hero Icon Pack](https://heroicons.com/) |
|
||||
| `title` | Title heading for the card. |
|
||||
| `subtitle` | Subtitle heading (supports Markdown). |
|
||||
| `url` | URL |
|
||||
+4
-16
@@ -1,18 +1,7 @@
|
||||
---
|
||||
title: Learn JavaScript
|
||||
summary: Easily learn JavaScript in 10 minutes!
|
||||
title: Embed Media
|
||||
summary: Easily embed audio and video media in Hugo sites
|
||||
date: 2023-10-24
|
||||
type: docs
|
||||
math: false
|
||||
tags:
|
||||
- JavaScript
|
||||
image:
|
||||
caption: 'Embed rich media such as videos and LaTeX math'
|
||||
content_meta:
|
||||
content_type: 'Tutorial'
|
||||
difficulty: 'Beginner'
|
||||
prerequisites: ['HTML']
|
||||
trending: true
|
||||
---
|
||||
|
||||
[Hugo Blox Builder](https://hugoblox.com) is designed to give technical content creators a seamless experience. You can focus on the content and the Hugo Blox Builder which this template is built upon handles the rest.
|
||||
@@ -71,9 +60,8 @@ Hugo Blox Builder supports a Markdown extension for $\LaTeX$ math. You can enabl
|
||||
|
||||
To render _inline_ or _block_ math, wrap your LaTeX math with `{{</* math */>}}$...${{</* /math */>}}` or `{{</* math */>}}$$...$${{</* /math */>}}`, respectively.
|
||||
|
||||
{{% callout note %}}
|
||||
We wrap the LaTeX math in the Hugo Blox _math_ shortcode to prevent Hugo rendering our math as Markdown.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> We wrap the LaTeX math in the Hugo Blox _math_ shortcode to prevent Hugo rendering our math as Markdown. This callout now uses the standard Markdown alert syntax!
|
||||
|
||||
Example **math block**:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Steps
|
||||
linkTitle: Steps
|
||||
---
|
||||
|
||||
Display a series of steps, such as for a tutorial.
|
||||
|
||||
## Example
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Step 1
|
||||
|
||||
The first step here...
|
||||
|
||||
### Step 2
|
||||
|
||||
The second step here...
|
||||
|
||||
### Step 3
|
||||
|
||||
The third step here...
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Use the Markdown level-3 headings to represent step titles within the `steps` shortcode:
|
||||
|
||||
```
|
||||
{{%/* steps */%}}
|
||||
|
||||
### Step 1
|
||||
|
||||
The first step here...
|
||||
|
||||
### Step 2
|
||||
|
||||
The second step here...
|
||||
|
||||
### Step 3
|
||||
|
||||
The third step here...
|
||||
|
||||
{{%/* /steps */%}}
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Spoilers
|
||||
---
|
||||
|
||||
A Hugo shortcode to toggle collapsible content.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Example
|
||||
|
||||
{{< spoiler text="Click to view the spoiler" >}}
|
||||
You found me!
|
||||
|
||||
Markdown is **supported**.
|
||||
{{< /spoiler >}}
|
||||
|
||||
## Usage
|
||||
|
||||
````
|
||||
{{</* spoiler text="Click to view the spoiler" */>}}
|
||||
|
||||
This is the content of the details.
|
||||
|
||||
Markdown is **supported**.
|
||||
|
||||
{{</* /spoiler */>}}
|
||||
````
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Project Structure
|
||||
weight: 1
|
||||
---
|
||||
|
||||
## Folder Structure
|
||||
|
||||
There are **4 main folders for Hugo-based sites**:
|
||||
|
||||
- `content/` for your Markdown-formatted content files (homepage, etc.)
|
||||
- `_index.md` the homepage (**Hugo requires that the homepage and archive pages have an underscore prefix**)
|
||||
- `assets/`
|
||||
- `media/` for your media files (images, videos)
|
||||
- `icons/custom/` upload any custom SVG icons you want to use
|
||||
- `config/_default/` for your site configuration files
|
||||
- `hugo.yaml` to configure Hugo (site title, URL, Hugo options, setup per-folder page features)
|
||||
- `module.yaml` to install or uninstall Hugo themes and plugins
|
||||
- `params.yaml` to configure Hugo Blox options (SEO, analytics, site features)
|
||||
- `menus.yaml` to configure your menu links (if the menu is enabled in `params.yaml`)
|
||||
- `languages.yaml` to configure your site's language or to set language-specific options in a multilingual site
|
||||
- `static/uploads/` for any files you want visitors to download, such as a PDF
|
||||
- `go.mod` sets the version of Hugo themes/plugins which your site uses
|
||||
|
||||
|
||||
## Hugo File Naming Convention
|
||||
|
||||
Hugo gives us two options to name standard page files: as `TITLE/index.md` or `TITLE.md` where `TITLE` is your page name.
|
||||
|
||||
The page name should be lowercase and using hyphens (`-`) instead of spaces.
|
||||
|
||||
Both approaches result in the same output, so you can choose your preferred approach to naming and organizing files. A benefit to the folder-based approach is that all your page's files (such as images) are self-contained within the page's folder, so it's more portable if you wish to share the original Markdown page with someone.
|
||||
|
||||
The homepage is a special case as **Hugo requires the homepage and listing pages to be named** `_index.md`.
|
||||
|
||||
## Docs Navigation
|
||||
|
||||
The docs navigation is automatically generated based on the content in the `docs/` folder and is sorted alphabetically.
|
||||
|
||||
The order of pages can be changed by adding the `weight` parameter in the front matter of your Markdown files.
|
||||
|
||||
In the example below, the `example.md` page will appear before the `test.md` page as it has a lower `weight`:
|
||||
|
||||
Page `example.md`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Example
|
||||
weight: 1
|
||||
---
|
||||
```
|
||||
|
||||
Page `test.md`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Test
|
||||
weight: 2
|
||||
---
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
linkTitle: Reference
|
||||
title: Reference
|
||||
---
|
||||
|
||||
This section covers reference docs.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Customizing Hugo
|
||||
linkTitle: Customization
|
||||
weight: 1
|
||||
---
|
||||
|
||||
View the full docs at https://docs.hugoblox.com/getting-started/customize/
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Internationalization (i18n)
|
||||
---
|
||||
|
||||
Hugo Blox enables you to easily edit the interface text as well as translating your site into multiple languages using Hugo's [multilingual](https://gohugo.io/content-management/multilingual/) feature.
|
||||
|
||||
<!--more-->
|
||||
|
||||
View the full docs at https://docs.hugoblox.com/reference/language/
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 170 KiB |
@@ -1,238 +0,0 @@
|
||||
---
|
||||
title: Learn Python
|
||||
summary: Easily learn Python in 10 minutes!
|
||||
date: 2023-10-24
|
||||
type: docs
|
||||
math: false
|
||||
tags:
|
||||
- Python
|
||||
image:
|
||||
caption: 'Embed rich media such as videos and LaTeX math'
|
||||
content_meta:
|
||||
content_type: 'Course'
|
||||
difficulty: 'Intermediate'
|
||||
prerequisites: ['SQL']
|
||||
trending: false
|
||||
---
|
||||
|
||||
[Hugo Blox Builder](https://hugoblox.com) is designed to give technical content creators a seamless experience. You can focus on the content and the Hugo Blox Builder which this template is built upon handles the rest.
|
||||
|
||||
**Embed videos, podcasts, code, LaTeX math, and even test students!**
|
||||
|
||||
On this page, you'll find some examples of the types of technical content that can be rendered with Hugo Blox.
|
||||
|
||||
## Video
|
||||
|
||||
Teach your course by sharing videos with your students. Choose from one of the following approaches:
|
||||
|
||||
{{< youtube D2vj0WcvH5c >}}
|
||||
|
||||
**Youtube**:
|
||||
|
||||
{{</* youtube w7Ft2ymGmfc */>}}
|
||||
|
||||
**Bilibili**:
|
||||
|
||||
{{</* bilibili id="BV1WV4y1r7DF" */>}}
|
||||
|
||||
**Video file**
|
||||
|
||||
Videos may be added to a page by either placing them in your `assets/media/` media library or in your [page's folder](https://gohugo.io/content-management/page-bundles/), and then embedding them with the _video_ shortcode:
|
||||
|
||||
{{</* video src="my_video.mp4" controls="yes" */>}}
|
||||
|
||||
## Podcast
|
||||
|
||||
You can add a podcast or music to a page by placing the MP3 file in the page's folder or the media library folder and then embedding the audio on your page with the _audio_ shortcode:
|
||||
|
||||
{{</* audio src="ambient-piano.mp3" */>}}
|
||||
|
||||
Try it out:
|
||||
|
||||
{{< audio src="ambient-piano.mp3" >}}
|
||||
|
||||
## Test students
|
||||
|
||||
Provide a simple yet fun self-assessment by revealing the solutions to challenges with the `spoiler` shortcode:
|
||||
|
||||
```markdown
|
||||
{{</* spoiler text="👉 Click to view the solution" */>}}
|
||||
You found me!
|
||||
{{</* /spoiler */>}}
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{< spoiler text="👉 Click to view the solution" >}} You found me 🎉 {{< /spoiler >}}
|
||||
|
||||
## Math
|
||||
|
||||
Hugo Blox Builder supports a Markdown extension for $\LaTeX$ math. You can enable this feature by toggling the `math` option in your `config/_default/params.yaml` file.
|
||||
|
||||
To render _inline_ or _block_ math, wrap your LaTeX math with `{{</* math */>}}$...${{</* /math */>}}` or `{{</* math */>}}$$...$${{</* /math */>}}`, respectively.
|
||||
|
||||
> [!NOTE]
|
||||
> We wrap the LaTeX math in the Hugo Blox _math_ shortcode to prevent Hugo rendering our math as Markdown. This callout now uses the standard Markdown alert syntax!
|
||||
|
||||
Example **math block**:
|
||||
|
||||
```latex
|
||||
{{</* math */>}}
|
||||
$$
|
||||
\gamma_{n} = \frac{ \left | \left (\mathbf x_{n} - \mathbf x_{n-1} \right )^T \left [\nabla F (\mathbf x_{n}) - \nabla F (\mathbf x_{n-1}) \right ] \right |}{\left \|\nabla F(\mathbf{x}_{n}) - \nabla F(\mathbf{x}_{n-1}) \right \|^2}
|
||||
$$
|
||||
{{</* /math */>}}
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{< math >}}
|
||||
$$\gamma_{n} = \frac{ \left | \left (\mathbf x_{n} - \mathbf x_{n-1} \right )^T \left [\nabla F (\mathbf x_{n}) - \nabla F (\mathbf x_{n-1}) \right ] \right |}{\left \|\nabla F(\mathbf{x}_{n}) - \nabla F(\mathbf{x}_{n-1}) \right \|^2}$$
|
||||
{{< /math >}}
|
||||
|
||||
Example **inline math** `{{</* math */>}}$\nabla F(\mathbf{x}_{n})${{</* /math */>}}` renders as {{< math >}}$\nabla F(\mathbf{x}_{n})${{< /math >}}.
|
||||
|
||||
Example **multi-line math** using the math linebreak (`\\`):
|
||||
|
||||
```latex
|
||||
{{</* math */>}}
|
||||
$$f(k;p_{0}^{*}) = \begin{cases}p_{0}^{*} & \text{if }k=1, \\
|
||||
1-p_{0}^{*} & \text{if }k=0.\end{cases}$$
|
||||
{{</* /math */>}}
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{< math >}}
|
||||
|
||||
$$
|
||||
f(k;p_{0}^{*}) = \begin{cases}p_{0}^{*} & \text{if }k=1, \\
|
||||
1-p_{0}^{*} & \text{if }k=0.\end{cases}
|
||||
$$
|
||||
|
||||
{{< /math >}}
|
||||
|
||||
## Code
|
||||
|
||||
Hugo Blox Builder utilises Hugo's Markdown extension for highlighting code syntax. The code theme can be selected in the `config/_default/params.yaml` file.
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
data = pd.read_csv("data.csv")
|
||||
data.head()
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
data = pd.read_csv("data.csv")
|
||||
data.head()
|
||||
```
|
||||
|
||||
## Inline Images
|
||||
|
||||
```go
|
||||
{{</* icon name="python" */>}} Python
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{< icon name="python" >}} Python
|
||||
|
||||
## Callouts & Alerts
|
||||
|
||||
Hugo Blox Builder now supports GitHub and Obsidian-style Markdown alerts for better content portability! These replace the old `callout` shortcode.
|
||||
|
||||
### Basic Callout Types
|
||||
|
||||
**Note** - For general information:
|
||||
|
||||
```markdown
|
||||
> [!NOTE]
|
||||
> This is a note callout with important information that users should know.
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is a note callout with important information that users should know.
|
||||
|
||||
**Tip** - For helpful advice:
|
||||
|
||||
```markdown
|
||||
> [!TIP]
|
||||
> Here's a helpful tip to make your workflow more efficient!
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Here's a helpful tip to make your workflow more efficient!
|
||||
|
||||
**Important** - For critical information:
|
||||
|
||||
```markdown
|
||||
> [!IMPORTANT]
|
||||
> This is critical information that users must understand to proceed.
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This is critical information that users must understand to proceed.
|
||||
|
||||
**Warning** - For potential issues:
|
||||
|
||||
```markdown
|
||||
> [!WARNING]
|
||||
> Be careful! This action might have unexpected consequences.
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Be careful! This action might have unexpected consequences.
|
||||
|
||||
**Caution** - For dangerous actions:
|
||||
|
||||
```markdown
|
||||
> [!CAUTION]
|
||||
> Danger! This operation is irreversible and could cause data loss.
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Danger! This operation is irreversible and could cause data loss.
|
||||
|
||||
### Custom Titles
|
||||
|
||||
You can customize the title of any callout:
|
||||
|
||||
```markdown
|
||||
> [!WARNING]+ Custom Warning Title
|
||||
> This warning has a custom title instead of just "Warning".
|
||||
```
|
||||
|
||||
> [!WARNING]+ Custom Warning Title
|
||||
> This warning has a custom title instead of just "Warning".
|
||||
|
||||
### Legacy Syntax (Deprecated)
|
||||
|
||||
The old shortcode syntax still works but shows a deprecation warning:
|
||||
|
||||
```markdown
|
||||
{{</* callout note */>}}
|
||||
This still works but is deprecated. Use the Markdown syntax above!
|
||||
{{</* /callout */>}}
|
||||
```
|
||||
|
||||
{{% callout note %}}
|
||||
This still works but is deprecated. Use the Markdown syntax above!
|
||||
{{% /callout %}}
|
||||
|
||||
### Benefits of the New Syntax
|
||||
|
||||
- **Portable**: Works with GitHub, Obsidian, and other Markdown processors
|
||||
- **Standard**: Uses widely-adopted Markdown alert syntax
|
||||
- **Clean**: No Hugo-specific shortcodes needed
|
||||
- **Future-proof**: Supported by the latest Hugo versions (0.132.0+)
|
||||
- **Multilingual**: Callout titles are automatically translated based on your site's language
|
||||
- English: "Note", "Tip", "Important", "Warning", "Caution"
|
||||
- Spanish: "Nota", "Consejo", "Importante", "Advertencia", "Precaución"
|
||||
- French: "Note", "Conseil", "Important", "Attention", "Prudence"
|
||||
- German: "Hinweis", "Tipp", "Wichtig", "Warnung", "Vorsicht"
|
||||
- Chinese: "注意", "提示", "重要", "警告", "小心"
|
||||
|
||||
## Did you find this page helpful? Consider sharing it 🙌
|
||||
@@ -60,9 +60,8 @@ projects:
|
||||
- example
|
||||
---
|
||||
|
||||
{{% callout note %}}
|
||||
Click on the **Slides** button above to view the built-in slides feature.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Click on the **Slides** button above to view the built-in slides feature.
|
||||
|
||||
Slides can be added in a few ways:
|
||||
|
||||
|
||||
@@ -81,12 +81,10 @@ projects:
|
||||
slides: ""
|
||||
---
|
||||
|
||||
{{% callout note %}}
|
||||
Click the _Cite_ button above to demo the feature to enable visitors to import publication metadata into their reference management software.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Click the _Cite_ button above to demo the feature to enable visitors to import publication metadata into their reference management software.
|
||||
|
||||
{{% callout note %}}
|
||||
Create your slides in Markdown - click the _Slides_ button to check out the example.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Create your slides in Markdown - click the _Slides_ button to check out the example.
|
||||
|
||||
Add the publication's **full text** or **supplementary notes** here. You can use rich formatting such as including [code, math, and images](https://docs.hugoblox.com/content/writing-markdown-latex/).
|
||||
@@ -73,12 +73,10 @@ projects: []
|
||||
slides: ""
|
||||
---
|
||||
|
||||
{{% callout note %}}
|
||||
Click the *Cite* button above to demo the feature to enable visitors to import publication metadata into their reference management software.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Click the *Cite* button above to demo the feature to enable visitors to import publication metadata into their reference management software.
|
||||
|
||||
{{% callout note %}}
|
||||
Create your slides in Markdown - click the *Slides* button to check out the example.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Create your slides in Markdown - click the *Slides* button to check out the example.
|
||||
|
||||
Add the publication's **full text** or **supplementary notes** here. You can use rich formatting such as including [code, math, and images](https://docs.hugoblox.com/content/writing-markdown-latex/).
|
||||
@@ -75,8 +75,7 @@ slides: ""
|
||||
|
||||
This work is driven by the results in my [previous paper](/publications/conference-paper/) on LLMs.
|
||||
|
||||
{{% callout note %}}
|
||||
Create your slides in Markdown - click the *Slides* button to check out the example.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> Create your slides in Markdown - click the *Slides* button to check out the example.
|
||||
|
||||
Add the publication's **full text** or **supplementary notes** here. You can use rich formatting such as including [code, math, and images](https://docs.hugoblox.com/content/writing-markdown-latex/).
|
||||
@@ -115,24 +115,21 @@ renders as
|
||||
|
||||
Use [callouts](https://docs.hugoblox.com/reference/markdown/#callouts) (aka _asides_, _hints_, or _alerts_) to draw attention to notes, tips, and warnings.
|
||||
|
||||
By wrapping a paragraph in `{{%/* callout note */%}} ... {{%/* /callout */%}}`, it will render as an aside.
|
||||
Use the `> [!NOTE]` syntax to create a callout.
|
||||
|
||||
```markdown
|
||||
{{%/* callout note */%}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{%/* /callout */%}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{% callout note %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
Or use the `warning` callout type so your readers don't miss critical details:
|
||||
|
||||
{{% callout warning %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!WARNING]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
## Did you find this page helpful? Consider sharing it 🙌
|
||||
@@ -13,24 +13,20 @@ Callouts are a useful feature to draw attention to important or related content
|
||||
|
||||
### Notes
|
||||
|
||||
{{% callout note %}}
|
||||
A Markdown callout is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> A Markdown callout is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
```markdown
|
||||
{{%/* callout note */%}}
|
||||
A Markdown callout is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{%/* /callout */%}}
|
||||
> [!NOTE]
|
||||
> A Markdown callout is useful for displaying notices, hints, or definitions to your readers.
|
||||
```
|
||||
|
||||
### Warnings
|
||||
|
||||
{{% callout warning %}}
|
||||
Here's some important information...
|
||||
{{% /callout %}}
|
||||
> [!WARNING]
|
||||
> Here's some important information...
|
||||
|
||||
```markdown
|
||||
{{%/* callout warning */%}}
|
||||
Here's some important information...
|
||||
{{%/* /callout */%}}
|
||||
> [!WARNING]
|
||||
> Here's some important information...
|
||||
```
|
||||
@@ -25,9 +25,9 @@ sections:
|
||||
text: "Read more"
|
||||
url: "/blog/"
|
||||
design:
|
||||
spacing:
|
||||
padding: [0, 0, 0, 0]
|
||||
margin: [0, 0, 0, 0]
|
||||
# spacing:
|
||||
# padding: [0, 0, 0, 0]
|
||||
# margin: [0, 0, 0, 0]
|
||||
# For full-screen, add `min-h-screen` below
|
||||
css_class: "dark"
|
||||
background:
|
||||
@@ -140,6 +140,6 @@ sections:
|
||||
design:
|
||||
card:
|
||||
# Card background color (CSS class)
|
||||
css_class: "bg-primary-700"
|
||||
css_class: "bg-primary-300"
|
||||
css_style: ""
|
||||
---
|
||||
@@ -115,24 +115,21 @@ renders as
|
||||
|
||||
Use [callouts](https://docs.hugoblox.com/reference/markdown/#callouts) (aka _asides_, _hints_, or _alerts_) to draw attention to notes, tips, and warnings.
|
||||
|
||||
By wrapping a paragraph in `{{%/* callout note */%}} ... {{%/* /callout */%}}`, it will render as an aside.
|
||||
Use the `> [!NOTE]` syntax to create a callout.
|
||||
|
||||
```markdown
|
||||
{{%/* callout note */%}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{%/* /callout */%}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
```
|
||||
|
||||
renders as
|
||||
|
||||
{{% callout note %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
Or use the `warning` callout type so your readers don't miss critical details:
|
||||
|
||||
{{% callout warning %}}
|
||||
A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
{{% /callout %}}
|
||||
> [!WARNING]
|
||||
> A Markdown aside is useful for displaying notices, hints, or definitions to your readers.
|
||||
|
||||
## Did you find this page helpful? Consider sharing it 🙌
|
||||
@@ -69,9 +69,8 @@ Hugo Blox Builder supports a Markdown extension for $\LaTeX$ math. You can enabl
|
||||
|
||||
To render _inline_ or _block_ math, wrap your LaTeX math with `{{</* math */>}}$...${{</* /math */>}}` or `{{</* math */>}}$$...$${{</* /math */>}}`, respectively.
|
||||
|
||||
{{% callout note %}}
|
||||
We wrap the LaTeX math in the Hugo Blox _math_ shortcode to prevent Hugo rendering our math as Markdown.
|
||||
{{% /callout %}}
|
||||
> [!NOTE]
|
||||
> We wrap the LaTeX math in the Hugo Blox _math_ shortcode to prevent Hugo rendering our math as Markdown.
|
||||
|
||||
Example **math block**:
|
||||
|
||||
|
||||
@@ -59,3 +59,5 @@ sections:
|
||||
color:
|
||||
light: "gray-50"
|
||||
dark: "gray-900"
|
||||
|
||||
|
||||
Reference in new issue
Block a user