This page uses a callout shortcode with a command shortcode nested in it.
The problem I’m seeing is that command is rendered on a separate line (the <p> tag for the text preceding it is closed when I look at the raw html)
And the text following the command shortcode isn’t converted to html
Here’s the source for command
{{ $englishCommand := "" }}
{{ $localCommand := ""}}
{{ $imageName := "" }}
{{ $version := "" }}
{{ $platform := "" }}
{{ if .IsNamedParams }}
{{ if .Get "english-command" }}{{ $englishCommand = .Get "english-command" }}{{ end }}
{{ $localCommand = (.Get "local-command" | default $englishCommand) }}
{{ $imageName = (.Get "image-name" | default $englishCommand) }}
{{ $version = (.Get "version" | default (string .Site.Params.latestVersion)) }}
{{ $platform = (.Get "platform" | default "win") }}
{{ else }}
{{ $englishCommand = .Get 0 }}
{{ $localCommand = (.Get 1 | default $englishCommand) }}
{{ $imageName = (.Get 2 | default $englishCommand)}}
{{ $version = (.Get 3 | default (string .Site.Params.latestVersion)) }}
{{ $platform = (.Get 4 | default "win") }}
{{ end }}
<a href=".../{{$version}}/help/{{.Site.Params.docLang}}/index.htm#commands/{{lower $englishCommand }}.htm" target="_blank">
<span style="display: inline-block;" class="unfurl-link">
<strong>
<img style="
margin-top: -4px;
padding-top: 2px;
padding-bottom: 2px;
height: 24px;
vertical-align: middle;" src=".../{{$version}}/help/{{.Site.Params.docLang}}/image/command_icons/{{lower $imageName }}.png"
onerror="this.onerror=null;this.style.display='none';"
>
{{ $localCommand }}
</strong>
<script>
var parser = new DOMParser();
var href = "https:.../{{$version}}/help/{{.Site.Params.docLang}}/unfurl/{{lower $englishCommand }}.htm"
var scriptTag = document.getElementsByTagName('script');
scriptTag = scriptTag[scriptTag.length - 1];
var parentTag = scriptTag.parentNode;
function addPreviewDiv(parent){
const previewElement = document.createElement("span");
previewElement.classList.add("cmd_tooltip");
previewElement.classList.add("arrow-top");
const iframeElement = document.createElement("iframe");
iframeElement.src = href;
iframeElement.style.width = "100%";
iframeElement.style.height = "100%";
iframeElement.style.border = "none";
previewElement.appendChild(iframeElement);
parent.appendChild(previewElement)
}
addPreviewDiv(parentTag)
</script>
</span>
</a>
Here’s the source for callout
{{- $inner := .Inner | .Page.RenderString -}}
{{- $iconMap := dict "note" "fas fa-pencil-alt fa-fw" -}}
{{- $iconMap = dict "abstract" "fas fa-list-ul fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "info" "fas fa-info-circle fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "tip" "fas fa-lightbulb fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "success" "fas fa-check-circle fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "question" "fas fa-question-circle fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "warning" "fas fa-exclamation-triangle fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "failure" "fas fa-times-circle fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "danger" "fas fa-skull-crossbones fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "bug" "fas fa-bug fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "example" "fas fa-list-ol fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "quote" "fas fa-quote-right fa-fw" | merge $iconMap -}}
{{- $iconMap = dict "new" "fas fa-plus fa-fw" | merge $iconMap -}}
{{- $iconDetails := "fas fa-angle-right fa-fw" -}}
{{- $type := .Get "type" | default "note" -}}
<div class="details call-out {{ $type }}{{ if .Get `open` | ne false }} open{{ end }}">
<div class="details-summary call-out-title">
<i class="icon {{ index $iconMap $type | default (index $iconMap "note") }}"></i>{{ .Get "title" | default (T $type) }}<i class="details-icon {{ $iconDetails }}"></i>
</div>
<div class="details-content">
<div class="call-out-content">
{{- $inner -}}
</div>
</div>
</div>
irkode
October 1, 2025, 8:41pm
2
please post the source code of the page
Here:
{{< call-out danger "Testing commands and text styling inside callouts" >}}
This is a normal line of text.
This is a line with **some bold text**.
This is a line with *some italic text*.
This is a line with a command {{< command english-command="DragMode" local-command="DragMode">}} and some **bold** and some *italic* text.
{{< /call-out >}}
irkode
October 2, 2025, 7:12am
4
You pass the inner content to .Renderstring. This will render markdown
Check your .Inner before you pass it and see how many line breaks you have. A double line break makes a paragraph
So you will have to
remove some more white space in command shortcode using {{- -}}
avoid newlines between tags some tags <strong><img style="
avoid blank lines in the javascript part.
also indentation (more than 4 spaces) might break things
Here’s the output of {{- warnf "INNER>%s<INNER" .Inner -}}
The text between the Markers is passed to RenderString
WARN INNER>
This is a normal line of text.
This is a line with **some bold text**.
This is a line with *some italic text*.
This is a line with a command
<a href="...//help//index.htm#commands/dragmode.htm" target="_blank">
<span style="display: inline-block;" class="unfurl-link">
<strong>
<img style="
margin-top: -4px;
padding-top: 2px;
padding-bottom: 2px;
height: 24px;
vertical-align: middle;" src="...//help//image/command_icons/dragmode.png"
onerror="this.onerror=null;this.style.display='none';"
>
DragMode
</strong>
<script>
var parser = new DOMParser();
var href = "https:...//help//unfurl/dragmode.htm"
var scriptTag = document.getElementsByTagName('script');
scriptTag = scriptTag[scriptTag.length - 1];
var parentTag = scriptTag.parentNode;
function addPreviewDiv(parent){
const previewElement = document.createElement("span");
previewElement.classList.add("cmd_tooltip");
previewElement.classList.add("arrow-top");
const iframeElement = document.createElement("iframe");
iframeElement.src = href;
iframeElement.style.width = "100%";
iframeElement.style.height = "100%";
iframeElement.style.border = "none";
previewElement.appendChild(iframeElement);
parent.appendChild(previewElement)
}
addPreviewDiv(parentTag)
</script>
</span>
</a> and some **bold** and some *italic* text.
<INNER
p.s. using {{<command>}} frequently will produce a lot of inline styles, js creating iframes - Guess you need that because of … just looks uncomfortable to me
Hmm, I don’t understand where to use this. Adding {{- around {{< command english-command="DragMode" local-command="DragMode">}} doesn’t seem to work
I also tried wrapping everything inside command.html in {{- but it errors out.
I also basically minified content of command.html (removed all new lines) but now it looks even weirder
Look at your call-out shortcode for an example of how to use {{- and -}} to remove whitespace.
If I replace this…
{{ $englishCommand := "" }}
{{ $localCommand := ""}}
{{ $imageName := "" }}
{{ $version := "" }}
{{ $platform := "" }}
{{ if .IsNamedParams }}
{{ if .Get "english-command" }}{{ $englishCommand = .Get "english-command" }}{{ end }}
{{ $localCommand = (.Get "local-command" | default $englishCommand) }}
{{ $imageName = (.Get "image-name" | default $englishCommand) }}
{{ $version = (.Get "version" | default (string .Site.Params.latestVersion)) }}
{{ $platform = (.Get "platform" | default "win") }}
{{ else }}
{{ $englishCommand = .Get 0 }}
{{ $localCommand = (.Get 1 | default $englishCommand) }}
{{ $imageName = (.Get 2 | default $englishCommand)}}
{{ $version = (.Get 3 | default (string .Site.Params.latestVersion)) }}
{{ $platform = (.Get 4 | default "win") }}
{{ end }}
With this…
{{ $englishCommand := or (.Get "english-command") (.Get 0) "" -}}
{{ $localCommand := or (.Get "local-command") (.Get 1) $englishCommand -}}
{{ $imageName := or (.Get "image-name") (.Get 2) $englishCommand -}}
{{ $version := or (.Get "version") (.Get 3) (site.Params.latestVersion | string) -}}
{{ $platform := or (.Get "platform") (.Get 4) "win" -}}
…everything works fine.
1 Like
irkode
October 3, 2025, 7:28am
8
with a standard hugo theme and just adding the shortcodes I also had the experience:
but the html that is generated contains various paragraphs between tags and within the js code ???
So I think we need to also remove these to have the complete command properly rendered
having just that one produces some bad results
{{< call-out danger "Testing commands and text styling inside callouts" >}}
{{< command english-command="DragMode" local-command="DragMode">}}
{{< /call-out >}}
just a command shortcode having just the command shortcode on a single line will not create a paragraph → so added a dummy <span class="noop"></span> at the beginning.
Here’s my version that looks fine at first sight
but it escapes the Javascript in the result and the iframe is not rendered (locally)
{{ $englishCommand := or (.Get "english-command") (.Get 0) "" -}}
{{ $localCommand := or (.Get "local-command") (.Get 1) $englishCommand -}}
{{ $imageName := or (.Get "image-name") (.Get 2) $englishCommand -}}
{{ $version := or (.Get "version") (.Get 3) (site.Params.latestVersion | string) -}}
{{ $platform := or (.Get "platform") (.Get 4) "win" -}}
<span class="noop"></span><a href=".../{{$version}}/help/{{.Site.Params.docLang}}/index.htm#commands/{{lower $englishCommand }}.htm" target="_blank">
<span style="display: inline-block;" class="unfurl-link">
<strong>
<img style="
margin-top: -4px;
padding-top: 2px;
padding-bottom: 2px;
height: 24px;
vertical-align: middle;" src=".../{{$version}}/help/{{.Site.Params.docLang}}/image/command_icons/{{lower $imageName }}.png"
onerror="this.onerror=null;this.style.display='none';"
>
{{ $localCommand }}
</strong><script>
var parser = new DOMParser();
var href = "https:.../{{$version}}/help/{{.Site.Params.docLang}}/unfurl/{{lower $englishCommand }}.htm"
var scriptTag = document.getElementsByTagName('script');
scriptTag = scriptTag[scriptTag.length - 1];
var parentTag = scriptTag.parentNode;
function addPreviewDiv(parent){
const previewElement = document.createElement("span");
previewElement.classList.add("cmd_tooltip");
previewElement.classList.add("arrow-top");
const iframeElement = document.createElement("iframe");
iframeElement.src = href;
iframeElement.style.width = "100%";
iframeElement.style.height = "100%";
iframeElement.style.border = "none";
previewElement.appendChild(iframeElement);
parent.appendChild(previewElement)
}
addPreviewDiv(parentTag)
</script>
</span>
</a>
sigh …
1 Like
irkode
October 3, 2025, 9:53am
9
mmh, guess I have may found the glitch <script> tag inside a <p> wont work
**test** {{< command ... >}} **bold**
you want it to be <p> TEST <a href ... script ...> BOLD</p>
guess for that the pop-up logic has to be changed or refactored.
I’m not entirely sure how this is meant to look or function, but this seemed to work for me:
git clone --single-branch -b hugo-forum-topic-56012 https://github.com/jmooring/hugo-testing hugo-forum-topic-56012
cd hugo-forum-topic-56012
hugo server
That’s not how it supposed to look. The content of the iframe should be shown in a popover on hove.
That functionality can be seen here: Rhino - Test for callout
With everything in command shortcode in a single line looks right , except for the hover functionality gone:
{{ $englishCommand := or (.Get "english-command") (.Get 0) "" -}}{{ $localCommand := or (.Get "local-command") (.Get 1) $englishCommand -}}{{ $imageName := or (.Get "image-name") (.Get 2) $englishCommand -}}{{ $version := or (.Get "version") (.Get 3) (site.Params.latestRhinoVersion | string) -}}{{ $platform := or (.Get "platform") (.Get 4) "win" -}}
<a href="https://docs.mcneel.com/rhino/{{$version}}/help/{{.Site.Params.docLang}}/index.htm#commands/{{lower $englishCommand }}.htm" target="_blank"><span style="display: inline-block;" class="unfurl-link"><strong> <img style=" margin-top: -4px; padding-top: 2px; padding-bottom: 2px; height: 24px; vertical-align: middle;" src="https://docs.mcneel.com/rhino/{{$version}}/help/{{.Site.Params.docLang}}/image/command_icons/{{lower $imageName }}.png" onerror="this.onerror=null;this.style.display='none';" > {{ $localCommand }}</strong><script>var parser = new DOMParser();var href = "https://docs.mcneel.com/rhino/{{$version}}/help/{{.Site.Params.docLang}}/unfurl/{{lower $englishCommand }}.htm";var scriptTag = document.getElementsByTagName('script');scriptTag = scriptTag[scriptTag.length - 1];var parentTag = scriptTag.parentNode;function addPreviewDiv(parent){ const previewElement = document.createElement("span");previewElement.classList.add("cmd_tooltip");previewElement.classList.add("arrow-top");const iframeElement = document.createElement("iframe");iframeElement.src = href;iframeElement.style.width = "100%";iframeElement.style.height = "100%";iframeElement.style.border = "none";previewElement.appendChild(iframeElement);parent.appendChild(previewElement)}addPreviewDiv(parentTag)</script></span></a>
Shows an error in the console:
Looking at the error you can see the href string in the script has turned into garbage
irkode
October 3, 2025, 5:33pm
13
Thats what I mentioned here: Nested shortcode rendering - #9 by irkode
you want to keep the paragraphs so **markdown** here {{< command english-command="DragMode" local-command="DragMode">}} and **markdown** here renders a single paragraph like
markdown POPUP and markdown here
BUT: your current implementation will render your <script> to embed the iframe within a <p> tag when you remove the blanks. That’s not allowed.
You will have to find another solution with a javascript outside, maybe css, and the iframe loading a huge page for a pop up looks a little weird.
I could think of
a build based solution where you use GetRemote to download the popup content and plug it in using css/javascript
with the iframe way: move the javascript to the caller when generating the page and adjust some parameters.
a minimal repo that is buildable having all your the needed stuff for the example will help.
I took another swing at this. The only way I can make it work, without following @irkode ’s guidance, is to disable the Goldmark Typographer extension, which isn’t great.
git clone --single-branch -b hugo-forum-topic-56012 https://github.com/jmooring/hugo-testing hugo-forum-topic-56012
cd hugo-forum-topic-56012
hugo server
The above produces valid HTML by moving the script element outside of the span and a elements.
1 Like
irkode
October 5, 2025, 2:31pm
15
the <script> has to be outside of the .Inner content so it won’t be converted to escaped HTML.
Here’s a brute force POC
moving out the script,
keeping the iframe logicand
no need to disable the Typographer extension
Steps done
I move <script> to the end of the shortcode.
I generate unique ID s for each command (using math.Counter)
I pass the list of Id s from the <command> shortcode to the call-out> shortcode. via `.Parent.Store.Set
In <call-out>I brute force duplicate the <script> for every ID
change element selection logic to GetElementById
ofc there’s something more to do …
the id calculation should depend on the target and or command …
we need to add the url to the store, too
externalize the script as generic function
and just duplicate the calls (or pass a list to generate)
code changes to have the the <command> callable without being wrapped in a <call-out>
…
command
{{ $englishCommand := or (.Get "english-command") (.Get 0) "" -}}
{{ $localCommand := or (.Get "local-command") (.Get 1) $englishCommand -}}
{{ $imageName := or (.Get "image-name") (.Get 2) $englishCommand -}}
{{ $version := or (.Get "version") (.Get 3) (site.Params.latestVersion | string) -}}
{{ $platform := or (.Get "platform") (.Get 4) "win" -}}
{{ $id := add "my-popup-" (math.Counter | string) -}}
{{ .Parent.Store.Add "ids" (slice $id) -}}
<a href="https://docs.mcneel.com/rhino/8/help/en-us/index.htm#commands/dragmode.htm" target="_blank">
<span id="{{$id}}" style="display: inline-block;" class="unfurl-link">
<strong>
<img alt="" style="
margin-top: -4px;
padding-top: 2px;
padding-bottom: 2px;
height: 24px;
vertical-align: middle;" src="https://docs.mcneel.com/rhino/8/help/en-us/image/command_icons/dragmode.png"
onerror="this.onerror=null;this.style.display='none';"
>
{{ $localCommand }}
</strong>
</span>
</a>
{{- /**/ -}}
call-out
{{- $type := .Get "type" | default "note" -}}
<div class="details call-out {{ $type }}{{ if .Get `open` | ne false }} open{{ end }}">
<div class="details-summary call-out-title">
<i class="icon {{ index $iconMap $type | default (index $iconMap "note") }}"></i>{{ .Get "title" | default (T $type) }}<i class="details-icon {{ $iconDetails }}"></i>
</div>
<div class="details-content">
<div class="call-out-content">
{{- $inner -}}
</div>
</div>
</div>
{{- range .Store.Get "ids" -}}
<script>
var parser = new DOMParser();
var href = 'https://docs.mcneel.com/rhino/8/help/en-us/unfurl/dragmode.htm';
var spanElement = document.getElementById({{.}});
function addPreviewDiv(spanElement){
const previewElement = document.createElement('span');
previewElement.classList.add('cmd_tooltip');
previewElement.classList.add('arrow-top');
const iframeElement = document.createElement('iframe');
iframeElement.src = href;
iframeElement.style.width = '100%';
iframeElement.style.height = '100%';
iframeElement.style.border = 'none';
previewElement.appendChild(iframeElement);
spanElement.appendChild(previewElement);
}
addPreviewDiv(spanElement);
</script>
{{- end -}}
Can you elaborate what are the downsides of disabling this? Can it be disabled for a particular shortcode only? or it’s a sitewide setting?
irkode:
Here’s a brute force POC
Thank you for the elaborate solution. However we have hundreds of shortcodes, which can nest command or other shortcodes some of which have javascript. Your solution seems to require changes to all of them.
irkode
October 6, 2025, 6:37pm
18
Indeed … another way would be a CSS/JS based solution.
maybe generating the popup content statically - if that does not have to be live and you have access to the content at build time.
…
anyhow @jmooring 's approach is the simplest one to get it running with a proper HTML structure
loosing the nice opening and ‘closing’ “quotes” (yes, a site wide setting)
The Goldmark Typographer extension is globally enabled or disabled. You can read what it does here: https://gohugo.io/configuration/markup/#typographer
If this were my site, I wouldn’t include any JS in the shortcodes. Instead, I’d create an empty container (i.e., a div) with some data attributes, then add the iframe using a global script that fires when the page is done loading.
2 Likes