New Hugo Post and Folder Hammerspoon

I took a crack at making a new Hammerspoon Lua script. When I have posts that contain images (like this one) or other files, in Hugo you’d create them in a folder like content/posts/2025-04-18_simple-title/index.md. That would then allow you to drop things like associated images, videos and other files with the post itself, keeping it as a self-contained unit.

The problem is: Any time I wanted to do that meant I had to open Finder or GhosTTY, navigate to my Projects folder, find my site folder, navigate into content/posts and then make a new directory. THEN I would have to start a new index.md file elsewhere and then save it in to that directory. So much tedium! Then I got the bright idea that I have Hammerspoon and I’ve been looking for more ideas on how to use the hyper key. To the bat-cave BBEdit!

After a bit of searching, I found a simple enough to understand “slugify” lua script, and after a bit of noodling through the docs, then asking Claude to critique my work, I had a pretty usable script! Behold!

-- Slugify function to convert text to URL-friendly format
function Slugify(text)
    if text == nil then
        return ""
    end

    -- Convert to lowercase
    local slug = string.lower(text)

    -- Replace spaces with hyphens
    slug = string.gsub(slug, "%s+", "-")

    -- Remove special characters
    slug = string.gsub(slug, "[^%w%-]", "")

    -- Remove consecutive hyphens
    slug = string.gsub(slug, "%-+", "-")

    -- Remove leading and trailing hyphens
    slug = string.gsub(slug, "^%-", "")
    slug = string.gsub(slug, "%-$", "")

    return slug
end

local function createHugoPostFolder()
    -- Get the current date in YYYY-MM-DD format
    local currentDate = os.date("%Y-%m-%d")

    -- Prompt user for post name
    local button, postName = hs.dialog.textPrompt(
    	"Create a new Hugo Post?", -- title
    	"Enter the title for your new post:", -- message
    	"", -- default
    	"Create", "Cancel" -- buttons
    )
    if postName == nil or postName == "" or button == "Cancel" then
        return
    end

    -- Slugify the post name
    local slug = Slugify(postName)

    -- Create folder path
    local hugoContentPath = os.getenv("HOME") .. "/Projects/degruchy.org/content/posts/"
    local folderName = currentDate .. "_" .. slug
    local fullPath = hugoContentPath .. folderName

    -- Create the directory
    local success, errorMessage = hs.fs.mkdir(fullPath)

    if success then
        hs.alert.show("Created folder: " .. folderName)

        -- Optionally create an index.md file in the folder
        local indexFile = io.open(fullPath .. "/index.md", "w")
        if indexFile then
            indexFile:write("---\n")
            indexFile:write("title: \"" .. postName .. "\"\n")
            indexFile:write("date: <# Date #> \n")
            indexFile:write("draft: true\n")
            indexFile:write("---\n\n")
            indexFile:write("Your content here.\n")
            indexFile:close()
        end
    else
        hs.alert.show("Error creating folder: " .. errorMessage)
    end
end

hs.hotkey.bind({"cmd", "alt", "shift", "ctrl"}, "N", createHugoPostFolder)

Now, I can press hyper+n, get prompted for a new post title, where I can write a normal title with capitals and more, and have an appropriately named folder and index.md file created for me, in the right spot and ready for me to fill it with stuff!

A picture of a Hammerspoon dialog window asking me for a title
Respond via email.