Files
2026-01-02 22:07:38 +00:00

541 lines
22 KiB
HTML

{{- $ctx := . -}}
{{- $hbxVerification := partial "functions/hbx_verify.html" $ctx -}}
{{- if ne $hbxVerification "verified" -}}
{{- errorf "Install HugoBlox to use the Notebook shortcode at: %s" $ctx.Position -}}
{{- end -}}
{{- $shortcodeName := $ctx.Name | default "notebook" -}}
{{- $src := "" -}}
{{- if $ctx.IsNamedParams -}}
{{- $src = $ctx.Get "src" | default ($ctx.Get "file") -}}
{{- else -}}
{{- $src = $ctx.Get 0 -}}
{{- end -}}
{{- if not $src -}}
{{- errorf "%s shortcode: provide a notebook path via positional argument or src=..." $shortcodeName -}}
{{- end -}}
{{- $displayName := $src -}}
{{- with urls.Parse $src -}}
{{- if .Path -}}
{{- $displayName = path.Base .Path -}}
{{- end -}}
{{- end -}}
{{- $title := $ctx.Get "title" | default $displayName -}}
{{- $languageOverride := $ctx.Get "language" -}}
{{- $showCode := $ctx.Get "show_code" | default true -}}
{{- $showMarkdown := $ctx.Get "show_markdown" | default true -}}
{{- $showOutputs := $ctx.Get "show_outputs" | default true -}}
{{- $showMetadata := $ctx.Get "show_metadata" | default false -}}
{{- $lineNumbers := $ctx.Get "line_numbers" | default false -}}
{{- $dense := $ctx.Get "dense" | default false -}}
{{- $maxOutputHeight := $ctx.Get "max_output_height" | default "26rem" -}}
{{- $sourceURL := $ctx.Get "source_url" -}}
{{- $showDownload := $ctx.Get "show_download" | default true -}}
{{- $downloadLabel := $ctx.Get "download_label" | default "Download notebook" -}}
{{- $emptyMessage := $ctx.Get "empty_message" | default "Notebook is empty or hidden by the current view options." -}}
{{- $id := delimit (slice "hb-notebook" (partial "functions/uid.html" $ctx)) "-" -}}
{{- $isRemote := and $src (ne (urls.Parse $src).Scheme "") -}}
{{- $normalizedSrc := $src -}}
{{- if not $isRemote -}}
{{- $normalizedSrc = strings.TrimPrefix "./" $normalizedSrc -}}
{{- $normalizedSrc = strings.TrimPrefix "/" $normalizedSrc -}}
{{- if $normalizedSrc -}}
{{- $normalizedSrc = path.Clean $normalizedSrc -}}
{{- end -}}
{{- if eq $normalizedSrc "." -}}
{{- $normalizedSrc = "" -}}
{{- end -}}
{{- if strings.HasPrefix $normalizedSrc ".." -}}
{{- errorf "%s shortcode: refusing to read outside the project for %q" $shortcodeName $src -}}
{{- end -}}
{{- end -}}
{{- $notebookContent := "" -}}
{{- $resource := dict -}}
{{- $resourceFound := false -}}
{{- if $isRemote -}}
{{- $remote := try (resources.GetRemote $src) -}}
{{- if $remote.Err -}}
{{- errorf "%s shortcode: unable to fetch remote notebook %q (%s)" $shortcodeName $src $remote.Err -}}
{{- else -}}
{{- $resource = $remote.Value -}}
{{- $resourceFound = true -}}
{{- $notebookContent = $resource.Content -}}
{{- if not $sourceURL -}}
{{- $sourceURL = $src -}}
{{- end -}}
{{- end -}}
{{- else -}}
{{- with $ctx.Page -}}
{{- with .Resources -}}
{{- with .GetMatch $normalizedSrc -}}
{{- $resource = . -}}
{{- $resourceFound = true -}}
{{- else -}}
{{- if not (strings.Contains $normalizedSrc "/") -}}
{{- with .GetMatch (printf "**/%s" $normalizedSrc) -}}
{{- $resource = . -}}
{{- $resourceFound = true -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if $resourceFound -}}
{{- $notebookContent = $resource.Content -}}
{{- if not $sourceURL -}}
{{- $sourceURL = $resource.RelPermalink -}}
{{- end -}}
{{- end -}}
{{- if not $notebookContent -}}
{{- $searchPaths := slice $normalizedSrc -}}
{{- $pageDir := "" -}}
{{- with $ctx.Page.File -}}
{{- $pageDir = path.Dir .Path -}}
{{- end -}}
{{- if $pageDir -}}
{{- $searchPaths = $searchPaths | append (path.Join $pageDir $normalizedSrc) -}}
{{- if not (strings.HasPrefix $normalizedSrc "content/") -}}
{{- $searchPaths = $searchPaths | append (printf "content/%s" (path.Join $pageDir $normalizedSrc)) -}}
{{- end -}}
{{- end -}}
{{- if not (strings.HasPrefix $normalizedSrc "content/") -}}
{{- $searchPaths = $searchPaths | append (printf "content/%s" $normalizedSrc) -}}
{{- end -}}
{{- if not (strings.HasPrefix $normalizedSrc "assets/") -}}
{{- $searchPaths = $searchPaths | append (printf "assets/%s" $normalizedSrc) -}}
{{- end -}}
{{- if not (strings.HasPrefix $normalizedSrc "static/") -}}
{{- $searchPaths = $searchPaths | append (printf "static/%s" $normalizedSrc) -}}
{{- end -}}
{{- range $candidate := $searchPaths }}
{{- if not $notebookContent -}}
{{- $fileAttempt := try (readFile $candidate) -}}
{{- if not $fileAttempt.Err -}}
{{- $notebookContent = $fileAttempt.Value -}}
{{- if and (strings.HasPrefix $candidate "static/") (not $sourceURL) -}}
{{- $public := strings.TrimPrefix $candidate "static" -}}
{{- if not (strings.HasPrefix $public "/") -}}
{{- $public = printf "/%s" $public -}}
{{- end -}}
{{- $sourceURL = relLangURL $public -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $notebookContent -}}
{{- $pagePath := "" -}}
{{- with $ctx.Page.File -}}
{{- $pagePath = .Path -}}
{{- end -}}
{{- errorf "%s shortcode: notebook %q could not be located relative to %s. Please check that you have removed \\.ipynb$ from ignoreFiles in your config/_default/hugo.yaml." $shortcodeName $src $pagePath -}}
{{- end -}}
{{- $parsed := dict -}}
{{- $parsedResult := try (transform.Unmarshal $notebookContent) -}}
{{- if $parsedResult.Err -}}
{{- errorf "%s shortcode: %q is not valid notebook JSON (%s)" $shortcodeName $src $parsedResult.Err -}}
{{- else -}}
{{- $parsed = $parsedResult.Value -}}
{{- end -}}
{{- $cells := slice -}}
{{- with index $parsed "cells" -}}
{{- if reflect.IsSlice . -}}
{{- $cells = . -}}
{{- end -}}
{{- end -}}
{{- $metadata := index $parsed "metadata" | default dict -}}
{{- $language := $languageOverride -}}
{{- if not $language -}}
{{- with index $metadata "language_info" -}}
{{- with index . "name" -}}
{{- $language = printf "%v" . -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $language -}}
{{- $language = "python" -}}
{{- end -}}
{{- $language = lower $language -}}
{{- $languageVersion := "" -}}
{{- with index $metadata "language_info" -}}
{{- with index . "version" -}}
{{- $languageVersion = printf "%v" . -}}
{{- end -}}
{{- end -}}
{{- $kernelDisplay := "" -}}
{{- with index $metadata "kernelspec" -}}
{{- with index . "display_name" -}}
{{- $kernelDisplay = printf "%v" . -}}
{{- end -}}
{{- end -}}
{{- $kernelName := "" -}}
{{- with index $metadata "kernelspec" -}}
{{- with index . "name" -}}
{{- $kernelName = printf "%v" . -}}
{{- end -}}
{{- end -}}
{{- $nbformat := "" -}}
{{- with index $parsed "nbformat" -}}
{{- $nbformat = printf "%v" . -}}
{{- end -}}
{{- with index $parsed "nbformat_minor" -}}
{{- if $nbformat -}}
{{- $nbformat = printf "%s.%v" $nbformat . -}}
{{- else -}}
{{- $nbformat = printf "%v" . -}}
{{- end -}}
{{- end -}}
{{- $cellCount := len $cells -}}
{{- $containerClass := "hb-notebook not-prose" -}}
{{- if $dense -}}
{{- $containerClass = printf "%s %s" $containerClass "hb-notebook--dense" -}}
{{- end -}}
{{- $subtitleParts := slice -}}
{{- if $language -}}
{{- $subtitleParts = $subtitleParts | append (strings.Title $language) -}}
{{- end -}}
{{- if $kernelDisplay -}}
{{- $subtitleParts = $subtitleParts | append (printf "Kernel: %s" $kernelDisplay) -}}
{{- end -}}
{{- if $nbformat -}}
{{- $subtitleParts = $subtitleParts | append (printf "nbformat %s" $nbformat) -}}
{{- end -}}
{{- if gt $cellCount 0 -}}
{{- $subtitleParts = $subtitleParts | append (printf "%d cells" $cellCount) -}}
{{- end -}}
{{- $metaItems := slice -}}
{{- $metaItems = $metaItems | append (dict "label" "Language" "value" (strings.Title $language)) -}}
{{- if $languageVersion -}}
{{- $metaItems = $metaItems | append (dict "label" "Version" "value" $languageVersion) -}}
{{- end -}}
{{- if or $kernelDisplay $kernelName -}}
{{- $metaItems = $metaItems | append (dict "label" "Kernel" "value" (strings.TrimSpace (printf "%s %s" $kernelDisplay $kernelName))) -}}
{{- end -}}
{{- if $nbformat -}}
{{- $metaItems = $metaItems | append (dict "label" "nbformat" "value" $nbformat) -}}
{{- end -}}
{{- with index $metadata "authors" -}}
{{- $authors := slice -}}
{{- if reflect.IsSlice . -}}
{{- range . -}}
{{- $name := "" -}}
{{- with index . "name" -}}
{{- $name = printf "%v" . -}}
{{- end -}}
{{- if not $name -}}
{{- $name = printf "%v" . -}}
{{- end -}}
{{- if $name -}}
{{- $authors = $authors | append $name -}}
{{- end -}}
{{- end -}}
{{- else -}}
{{- $authors = $authors | append (printf "%v" .) -}}
{{- end -}}
{{- if gt (len $authors) 0 -}}
{{- $metaItems = $metaItems | append (dict "label" "Authors" "value" (delimit $authors ", ")) -}}
{{- end -}}
{{- end -}}
{{- $sourceURLIsRemote := false -}}
{{- if $sourceURL -}}
{{- $sourceURLIsRemote = ne (urls.Parse $sourceURL).Scheme "" -}}
{{- if not $sourceURLIsRemote -}}
{{- if not (strings.HasPrefix $sourceURL "/") -}}
{{- $sourceURL = printf "/%s" $sourceURL -}}
{{- end -}}
{{- $sourceURL = relLangURL $sourceURL -}}
{{- end -}}
{{- end -}}
{{- $styleAttr := printf "--hb-notebook-output-max-height:%s;" $maxOutputHeight | safeCSS -}}
<div id="{{ $id }}" class="{{ $containerClass }}" data-hb-component="notebook" aria-label="Notebook {{ $title | plainify }}" style="{{ $styleAttr }}">
<div class="hb-notebook-header">
<div class="hb-notebook-heading">
<p class="hb-notebook-title">{{ $title }}</p>
{{- with $subtitleParts }}
<p class="hb-notebook-subtitle">{{ delimit . " · " }}</p>
{{- end }}
</div>
{{- if and $showDownload $sourceURL }}
<a class="hb-notebook-download" href="{{ $sourceURL | safeURL }}" {{ if $sourceURLIsRemote }}target="_blank" rel="noopener noreferrer"{{ else }}download{{ end }}>
{{ partial "functions/get_icon" (dict "name" "arrow-down-tray" "attributes" "class=\"w-4 h-4\"") }}
<span>{{ $downloadLabel }}</span>
</a>
{{- end }}
</div>
{{- if and $showMetadata (gt (len $metaItems) 0) }}
<dl class="hb-notebook-metadata">
{{- range $metaItems }}
{{- if .value }}
<div>
<dt>{{ .label }}</dt>
<dd>{{ .value }}</dd>
</div>
{{- end }}
{{- end }}
</dl>
{{- end }}
{{- $visibleScratch := newScratch -}}
{{- $visibleScratch.Set "visible" 0 -}}
<div class="hb-notebook-body">
{{- range $cells }}
{{- $cellType := index . "cell_type" | default "raw" -}}
{{- $cellMetadata := index . "metadata" | default dict -}}
{{- $cellTags := slice -}}
{{- with index $cellMetadata "tags" -}}
{{- if reflect.IsSlice . -}}
{{- range . -}}
{{- $cellTags = $cellTags | append (printf "%v" .) -}}
{{- end -}}
{{- else -}}
{{- $cellTags = $cellTags | append (printf "%v" .) -}}
{{- end -}}
{{- end -}}
{{- $executionCount := "" -}}
{{- with index . "execution_count" -}}
{{- $executionCount = printf "%v" . -}}
{{- end -}}
{{- $outputs := slice -}}
{{- with index . "outputs" -}}
{{- if reflect.IsSlice . -}}
{{- $outputs = . -}}
{{- end -}}
{{- end -}}
{{- $shouldRender := or
(and (eq $cellType "markdown") $showMarkdown)
(and (eq $cellType "raw") $showMarkdown)
(and (eq $cellType "code") (or $showCode (and $showOutputs (gt (len $outputs) 0))))
-}}
{{- if $shouldRender }}
{{- $visibleScratch.Add "visible" 1 -}}
<article class="hb-notebook-cell {{ cond (eq $cellType "code") "hb-notebook-cell--code" (cond (eq $cellType "markdown") "hb-notebook-cell--markdown" "hb-notebook-cell--raw") }}" data-cell-type="{{ $cellType }}">
<header class="hb-notebook-cell-header">
<span class="hb-notebook-pill">
{{- if eq $cellType "code" -}}
{{- printf "In [%s]" (cond $executionCount $executionCount " ") -}}
{{- else -}}
{{- strings.Title $cellType -}}
{{- end -}}
</span>
{{- if gt (len $cellTags) 0 }}
<div class="hb-notebook-tags">
{{- range $cellTags }}
<span>{{ . }}</span>
{{- end }}
</div>
{{- end }}
</header>
{{- if eq $cellType "markdown" }}
{{- $source := index . "source" -}}
{{- $sourceContent := "" -}}
{{- if reflect.IsSlice $source -}}
{{- $sourceContent = delimit $source "" -}}
{{- else if $source -}}
{{- $sourceContent = printf "%v" $source -}}
{{- end -}}
{{- $sourceContent = replace $sourceContent "\r\n" "\n" -}}
{{- if $sourceContent }}
<div class="hb-notebook-markdown prose dark:prose-invert">
{{ $sourceContent | markdownify }}
</div>
{{- end -}}
{{- else if eq $cellType "raw" }}
{{- $raw := index . "source" -}}
{{- $rawContent := "" -}}
{{- if reflect.IsSlice $raw -}}
{{- $rawContent = delimit $raw "" -}}
{{- else if $raw -}}
{{- $rawContent = printf "%v" $raw -}}
{{- end -}}
{{- if $rawContent }}
<pre class="hb-notebook-raw">{{ $rawContent | htmlEscape }}</pre>
{{- end -}}
{{- else if eq $cellType "code" }}
{{- $source := index . "source" -}}
{{- $sourceContent := "" -}}
{{- if reflect.IsSlice $source -}}
{{- $sourceContent = delimit $source "" -}}
{{- else if $source -}}
{{- $sourceContent = printf "%v" $source -}}
{{- end -}}
{{- $sourceContent = replace $sourceContent "\r\n" "\n" -}}
{{- $sourceContent = strings.TrimSuffix "\n" $sourceContent -}}
{{- $sourceContent = strings.TrimSuffix "\r" $sourceContent -}}
{{- if and $showCode $sourceContent }}
<div class="hb-notebook-code" data-language="{{ $language }}">
{{- $options := "" -}}
{{- if $lineNumbers -}}
{{- $options = "linenos=table" -}}
{{- end -}}
{{- $hl := highlight $sourceContent $language $options -}}
{{- if $hl -}}
{{ $hl | safeHTML }}
{{- else -}}
<pre><code>{{ $sourceContent | htmlEscape }}</code></pre>
{{- end -}}
</div>
{{- end -}}
{{- if and $showOutputs (gt (len $outputs) 0) }}
<div class="hb-notebook-outputs">
{{- range $outputs }}
{{- $outputType := index . "output_type" | default "" -}}
{{- if eq $outputType "stream" }}
{{- $text := index . "text" -}}
{{- $textContent := "" -}}
{{- if reflect.IsSlice $text -}}
{{- $textContent = delimit $text "" -}}
{{- else if $text -}}
{{- $textContent = printf "%v" $text -}}
{{- end -}}
<pre class="hb-notebook-output hb-notebook-output--stream"><code>{{ $textContent | htmlEscape }}</code></pre>
{{- else if eq $outputType "error" }}
{{- $ename := index . "ename" | default "" -}}
{{- $evalue := index . "evalue" | default "" -}}
{{- $trace := index . "traceback" | default slice -}}
{{- $traceContent := "" -}}
{{- if reflect.IsSlice $trace -}}
{{- $traceContent = delimit $trace "\n" -}}
{{- else if $trace -}}
{{- $traceContent = printf "%v" $trace -}}
{{- end -}}
<div class="hb-notebook-output hb-notebook-output--error">
<strong>{{ printf "%s: %s" $ename $evalue }}</strong>
{{- if $traceContent }}
<pre><code>{{ $traceContent | htmlEscape }}</code></pre>
{{- end -}}
</div>
{{- else }}
{{- $data := index . "data" | default dict -}}
{{- $outputScratch := newScratch -}}
{{- $outputScratch.Set "rendered" false -}}
{{- with index $data "text/html" }}
{{- $htmlPayload := "" -}}
{{- if reflect.IsSlice . -}}
{{- $htmlPayload = delimit . "" -}}
{{- else -}}
{{- $htmlPayload = printf "%v" . -}}
{{- end -}}
{{- if $htmlPayload }}
<div class="hb-notebook-output hb-notebook-output--html">{{ $htmlPayload | safeHTML }}</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with index $data "text/markdown" }}
{{- $markdownPayload := "" -}}
{{- if reflect.IsSlice . -}}
{{- $markdownPayload = delimit . "" -}}
{{- else -}}
{{- $markdownPayload = printf "%v" . -}}
{{- end -}}
{{- if $markdownPayload }}
<div class="hb-notebook-output hb-notebook-output--markdown prose dark:prose-invert">
{{ $markdownPayload | markdownify }}
</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with index $data "text/latex" }}
{{- $latexPayload := "" -}}
{{- if reflect.IsSlice . -}}
{{- $latexPayload = delimit . "" -}}
{{- else -}}
{{- $latexPayload = printf "%v" . -}}
{{- end -}}
{{- if $latexPayload }}
<div class="hb-notebook-output hb-notebook-output--latex">{{ printf "$$%s$$" $latexPayload | safeHTML }}</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with index $data "image/png" }}
{{- $pngPayload := printf "%v" . -}}
{{- if $pngPayload }}
<div class="hb-notebook-output hb-notebook-output--image">
<img src="data:image/png;base64,{{ $pngPayload }}" alt="Notebook output image" loading="lazy" decoding="async" />
</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with index $data "image/jpeg" }}
{{- $jpegPayload := printf "%v" . -}}
{{- if $jpegPayload }}
<div class="hb-notebook-output hb-notebook-output--image">
<img src="data:image/jpeg;base64,{{ $jpegPayload }}" alt="Notebook output image" loading="lazy" decoding="async" />
</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with index $data "image/svg+xml" }}
{{- $svgPayload := "" -}}
{{- if reflect.IsSlice . -}}
{{- $svgPayload = delimit . "" -}}
{{- else -}}
{{- $svgPayload = printf "%v" . -}}
{{- end -}}
{{- if $svgPayload }}
<div class="hb-notebook-output hb-notebook-output--image">{{ $svgPayload | safeHTML }}</div>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- with or (index $data "application/vnd.plotly.v1+json") (index $data "application/json") }}
{{- $jsonFormatted := . | jsonify (dict "indent" " ") -}}
<pre class="hb-notebook-output hb-notebook-output--json"><code>{{ $jsonFormatted }}</code></pre>
{{- $outputScratch.Set "rendered" true -}}
{{- end }}
{{- if not ($outputScratch.Get "rendered") }}
{{- with index $data "text/plain" }}
{{- $textPayload := "" -}}
{{- if reflect.IsSlice . -}}
{{- $textPayload = delimit . "" -}}
{{- else -}}
{{- $textPayload = printf "%v" . -}}
{{- end -}}
{{- if $textPayload }}
<pre class="hb-notebook-output hb-notebook-output--text"><code>{{ $textPayload | htmlEscape }}</code></pre>
{{- $outputScratch.Set "rendered" true -}}
{{- end -}}
{{- end }}
{{- end }}
{{- if not ($outputScratch.Get "rendered") }}
<pre class="hb-notebook-output hb-notebook-output--json"><code>{{ $data | jsonify | htmlEscape }}</code></pre>
{{- end }}
{{- end }}
{{- end }}
</div>
{{- end }}
{{- end }}
</article>
{{- end }}
{{- end }}
</div>
{{- if eq ($visibleScratch.Get "visible") 0 }}
<div class="hb-notebook-empty">{{ $emptyMessage }}</div>
{{- end }}
<div class="sr-only" data-hb-component="notebook" data-hb-version="1.0" data-hb-license="MIT">
Powered by Hugo Blox Kit - https://github.com/HugoBlox/kit
</div>
</div>