Skip to main content

Blowfish: VSCode edit button and galleries

·528 words·3 mins
loothi
Author
loothi

I’ve just added an “Edit in VSCode” button to my blog that appears when running the local server. Click it and the post opens directly in VSCode at the right location.

This implementation is based on Zoltan Toma’s excellent guide - you should read his article first as he’s funnier than me and explains everything thoroughly.

Why not use Blowfish’s built-in edit button?
#

Blowfish has an edit button feature, but it’s designed for GitHub/GitLab links. Using the vscode:// protocol triggers Hugo’s security and replaces the URL with #ZgotmplZ. That string means ‘unsafe, I won’t show it to you’.

Implementation
#

The implementation uses Hugo’s environment-specific configs and the vscode://file URL scheme.

Environment Configs
#

Created config/development/hugo.toml to enable the button in dev mode:

[params.editPost]
  enabled = true
  URL = "vscode://file"
  workingDir = "/Users/yourname/dev/yourwebsite"

And config/production/hugo.toml to disable it in production:

[params.editPost]
  enabled = false

Edit Button Partial
#

Created layouts/partials/edit_post.html with a conditional button that constructs the VSCode URL:

{{ if site.Params.editPost.enabled }}
  {{ with .File }}
    {{ $filePath := path.Join site.Params.editPost.workingDir .Path }}
    {{ $vscodeURL := printf "%s%s" site.Params.editPost.URL $filePath | safeURL }}
    <a href="{{ $vscodeURL }}" class="edit-post-link">
      <!-- SVG icon and "Edit in VSCode" text -->
    </a>
  {{ end }}
{{ end }}

Link Renderer Override#

Added layouts/_default/_markup/render-link.html to support the vscode:// protocol :

{{- if or (strings.HasPrefix .Destination "http:") 
          (strings.HasPrefix .Destination "https:") 
          (strings.HasPrefix .Destination "vscode:") }}
  target="_blank"
{{- end }}

Template Integration
#

I copied themes/blowfish/layouts/_default/single.html to layouts/_default/single.html and added one line ({{ partial "edit_post.html" . }}) in the article header section, leaving everything else identical.

This works because Blowfish is a git submodule, so editing theme files directly would lose changes on updates. Hugo checks layouts/ in my project before themes/blowfish/layouts/, which means I can pull theme updates without losing customisations.

Running different versions
#

Development server with drafts and edit buttons

	hugo server --environment development -D

Production server for testing (no edit buttons)

	hugo server --environment production

Build setup for live on Cloudflare

    hugo --environment production --minify

This ensures the edit button stays hidden on the live site.

Photo gallery setup reminder#

Since I’m documenting site improvements, here’s a quick reminder for future me about handling images and galleries.

The gallery pattern I should use#

{{< gallery >}}
  <img src="images/photo.jpg" class="grid-w33" alt="Description" data-zoom-src="images/photo-scaled.jpg" />
{{< /gallery >}}

What each part does:

  • {{< gallery >}} - Hugo shortcode that wraps images in a grid layout with medium-zoom enabled
  • src="images/photo.jpg" - The display/thumbnail version (usually 1024px wide)
  • data-zoom-src="images/photo-scaled.jpg" - Full-size image that opens when clicked (2048px or original)
  • class="grid-w33" - Makes a 3-column grid layout
  • alt="Description" - Descriptive text for accessibility

Why this works for my site
#

My site uses Hugo’s page bundles pattern where each post has its own directory (content/posts/YYYY/slug/index.md) with images stored in an images/ subdirectory. Blowfish includes the medium-zoom library, so clicking any image (except those with class="nozoom") opens a full-size overlay. This is particularly useful for my WordPress migration, where old posts used clickable image wrappers like [![](thumbnail)](hard-coded-url) which break Hugo’s render-image hook - the gallery shortcode is the modern replacement for that pattern.

Now why are my “Newer” and “Older” links arse-backwards 🤨#