|
| 1 | +{{- /* |
| 2 | + Hextra asciinema player shortcode (Bootstrap 5 port). |
| 3 | + Embeds an asciinema-player for terminal recordings. |
| 4 | + |
| 5 | + @param {string} file Path or URL of the .cast file (or positional param 0). |
| 6 | + @param {string} theme Player theme (default "asciinema"). |
| 7 | + @param {number} speed Playback speed (default 1). |
| 8 | + @param {bool} autoplay Auto-play on load (default false). |
| 9 | + @param {bool} loop Loop playback (default false). |
| 10 | + @param {string} poster Poster / thumbnail specification. |
| 11 | + @param {string} markers Comma-separated time markers. |
| 12 | + |
| 13 | + @example {{< hextra/asciinema file="demo.cast" >}} |
| 14 | +*/ -}} |
| 15 | + |
| 16 | +{{- $castFile := .Get "file" | default (.Get 0) -}} |
| 17 | +{{- $theme := .Get "theme" | default "asciinema" -}} |
| 18 | +{{- $speed := .Get "speed" | default 1 -}} |
| 19 | +{{- $autoplay := .Get "autoplay" | default false -}} |
| 20 | +{{- $loop := .Get "loop" | default false -}} |
| 21 | +{{- $poster := .Get "poster" | default "" -}} |
| 22 | +{{- $markers := .Get "markers" | default "" -}} |
| 23 | + |
| 24 | +{{- /* Handle file path: support local files, absolute paths, and remote URLs */ -}} |
| 25 | +{{- $isLocal := not (urls.Parse $castFile).Scheme -}} |
| 26 | +{{- $isPage := and (eq .Page.Kind "page") (not .Page.BundleType) -}} |
| 27 | + |
| 28 | +{{- if $isLocal -}} |
| 29 | + {{- $found := false -}} |
| 30 | + |
| 31 | + {{- if not $isPage -}} |
| 32 | + {{- with .Page.Resources.Get $castFile -}} |
| 33 | + {{- $castFile = .RelPermalink -}} |
| 34 | + {{- $found = true -}} |
| 35 | + {{- end -}} |
| 36 | + {{- end -}} |
| 37 | + |
| 38 | + {{- if not $found -}} |
| 39 | + {{- with resources.Get $castFile -}} |
| 40 | + {{- $castFile = .RelPermalink -}} |
| 41 | + {{- $found = true -}} |
| 42 | + {{- end -}} |
| 43 | + {{- end -}} |
| 44 | + |
| 45 | + {{- if not $found -}} |
| 46 | + {{- if hasPrefix $castFile "/" -}} |
| 47 | + {{- $castFile = relURL (strings.TrimPrefix "/" $castFile) -}} |
| 48 | + {{- else -}} |
| 49 | + {{- $castFile = relURL $castFile -}} |
| 50 | + {{- end -}} |
| 51 | + {{- end -}} |
| 52 | +{{- end -}} |
| 53 | + |
| 54 | +{{- /* Build marker configuration using jsonify for proper escaping */ -}} |
| 55 | +{{- $markerConfig := "" -}} |
| 56 | +{{- if $markers -}} |
| 57 | + {{- $markerParts := slice -}} |
| 58 | + {{- range (split $markers ",") -}} |
| 59 | + {{- $item := trim . " " -}} |
| 60 | + {{- $colonIndex := findRE ":" $item -}} |
| 61 | + {{- if $colonIndex -}} |
| 62 | + {{- $pair := split $item ":" -}} |
| 63 | + {{- if ge (len $pair) 2 -}} |
| 64 | + {{- $time := float (trim (index $pair 0) " ") -}} |
| 65 | + {{- $label := trim (index $pair 1) " " -}} |
| 66 | + {{- $markerParts = $markerParts | append (slice $time $label) -}} |
| 67 | + {{- end -}} |
| 68 | + {{- else -}} |
| 69 | + {{- $markerParts = $markerParts | append (float $item) -}} |
| 70 | + {{- end -}} |
| 71 | + {{- end -}} |
| 72 | + {{- $markerConfig = $markerParts | jsonify -}} |
| 73 | +{{- end -}} |
| 74 | + |
| 75 | +{{- /* Mark page as using asciinema for conditional script loading */ -}} |
| 76 | +{{- .Page.Store.Set "hasAsciinema" true -}} |
| 77 | + |
| 78 | +<div class="hextra-asciinema-player" |
| 79 | + role="region" |
| 80 | + aria-label="{{ (T "terminalRecording") | default "Terminal recording" }}" |
| 81 | + data-cast-file="{{ $castFile }}" |
| 82 | + data-theme="{{ $theme }}" |
| 83 | + data-speed="{{ $speed }}" |
| 84 | + data-autoplay="{{ $autoplay }}" |
| 85 | + data-loop="{{ $loop }}" |
| 86 | + {{- if ne $poster "" }} data-poster="{{ $poster | safeURL }}"{{- end -}} |
| 87 | + {{- if $markerConfig }} data-markers='{{ $markerConfig }}'{{- end -}}> |
| 88 | +</div> |
| 89 | + |
| 90 | +{{- /* Include asciinema-player assets once per page */ -}} |
| 91 | +{{- if not (.Page.Store.Get "hextraAsciinemaLoaded") -}} |
| 92 | + {{- .Page.Store.Set "hextraAsciinemaLoaded" true -}} |
| 93 | +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/asciinema-player@3/dist/bundle/asciinema-player.css"> |
| 94 | +<script src="https://cdn.jsdelivr.net/npm/asciinema-player@3/dist/bundle/asciinema-player.min.js"></script> |
| 95 | +<script> |
| 96 | +document.addEventListener('DOMContentLoaded',function(){ |
| 97 | + document.querySelectorAll('.hextra-asciinema-player').forEach(function(el){ |
| 98 | + var opts={}; |
| 99 | + if(el.dataset.theme) opts.theme=el.dataset.theme; |
| 100 | + if(el.dataset.speed) opts.speed=parseFloat(el.dataset.speed); |
| 101 | + if(el.dataset.autoplay==='true') opts.autoPlay=true; |
| 102 | + if(el.dataset.loop==='true') opts.loop=true; |
| 103 | + if(el.dataset.poster) opts.poster=el.dataset.poster; |
| 104 | + if(el.dataset.markers){try{opts.markers=JSON.parse(el.dataset.markers);}catch(e){}} |
| 105 | + AsciinemaPlayer.create(el.dataset.castFile,el,opts); |
| 106 | + }); |
| 107 | +}); |
| 108 | +</script> |
| 109 | +{{- end -}} |
0 commit comments