Skip to content

Image Alt Textify

Have you ever stumbled upon a beautiful image in a Markdown file and wondered what it depicts? Or perhaps you’re aiming to make your content more accessible by providing descriptive alt text for all the visuals? 🖼️✨

In this sample, we’ll dive into a GenAIScript tool that automates the generation of alt text for images in Markdown files, making your content more inclusive and SEO-friendly!

👩‍💻 Understanding the Script Code

This script is composed of TypeScript code, designed to run with the GenAIScript CLI. Let’s break it down:

script({
title: "Image Alt Textify",
description: "Generate alt text for images in markdown files",
model: "openai:gpt-4o",
parameters: {
docs: {
type: "string",
description: "path to search for markdown files",
default: "**.{md,mdx}",
},
force: {
type: "boolean",
description: "regenerate all descriptions",
default: false,
},
assets: {
type: "string",
description: "image assets path",
default: "./slides/public",
},
},
})

Here we declare the script with a title and description, specifying it uses OpenAI’s GPT-4 model. We also set parameters for the file paths, choice to regenerate all descriptions, and the assets path.

Next, we extract environmental variables:

const { docs, force, assets } = env.vars

Configuration is crucial, as it allows customization of the script’s behavior.

Now, let’s create a helper function:

const resolveUrl = (filename: string, url: string) =>
/^\//.test(url)
? path.join(assets, url.slice(1))
: /^\.\//.test(url)
? path.join(path.dirname(filename), url.slice(2))
: url

This function resolves the URL for the image, determining its absolute path based on different types of relative URLs.

Following that, we define a regular expression to find images in Markdown:

const rx = force
? /!\[[^\]]*\]\(([^\)]+\.(png|jpg))\)/g
: /!\[\s*\]\(([^\)]+\.(png|jpg))\)/g

This regex captures different scenarios based on whether we want to force regenerate all image descriptions or just the ones missing alt text.

We then use this regex to find relevant files:

const { files } = await workspace.grep(rx, docs, { readText: true })
console.log(`found ${files.length} files`)

The script uses workspace.grep to find all occurrences of the regex pattern in the specified documents.

For each image URL found, we generate alt text:

for (const file of files) {
const { filename, content } = file
// ...
// runPrompt is a faux function representing the LLM query
// ...
imgs[url] = text.replace(/\[/g, "")
}

In a loop, we process each file, generate the alt text using an AI model, and store it, making sure to strip out any brackets that could interfere with Markdown syntax.

Finally, we update the Markdown content with the generated alt text:

const newContent = content.replace(
rx,
(m, url) => `![${imgs[url] ?? ""}](${url})`
)
if (newContent !== content) await workspace.writeText(filename, newContent)

We replace the placeholder in the original content with the alt text and save the updated file.

💻 How to Run the Script

To run this script, you’ll need the GenAIScript CLI. If you haven’t installed it yet, check out the installation guide.

Once you have the CLI, you can run the script with the following command:

Terminal window
genaiscript run image-alt-textify

This command will execute the script using the filename without the .genai.mts extension. The script will search for Markdown files, generate alt text for images without descriptions or update existing ones if the force option is used.

🧑‍💼 Make sure to navigate to the directory where your Markdown files are located before running the script.

That’s it! You now have the power to enhance your Markdown files with descriptive alt text, making your content more accessible and engaging for all your readers.

Happy coding! 🚀

Full source (GitHub)

iat.genai.mts
/*
* Markdown image alt text updater
*/
script({
title: "Image Alt Textify",
description: "Generate alt text for images in markdown files",
parameters: {
docs: {
type: "string",
description: "path to search for markdown files",
default: "**.{md,mdx}",
},
force: {
type: "boolean",
description: "regenerate all descriptions",
default: false,
},
assets: {
type: "string",
description: "image assets path",
default: "./slides/public",
},
},
})
/** ------------------------------------------------
* Configuration
*/
const { docs, force, assets } = env.vars
/** ------------------------------------------------
* Helper functions (update as needed)
*/
/**
* Return the resolved url for the image
*/
const resolveUrl = (filename: string, url: string) =>
/^\//.test(url)
? path.join(assets, url.slice(1))
: /^\.\//.test(url)
? path.join(path.dirname(filename), url.slice(2))
: url
/** ------------------------------------------------
* Collect files
*/
// search for ![](...) in markdown files and generate alt text for images
const rx = force // upgrade all urls
? // match ![alt](url) with any alt
/!\[[^\]]*\]\(([^\)]+\.(png|jpg))\)/g
: // match ![alt](url) where alt is empty
/!\[\s*\]\(([^\)]+\.(png|jpg))\)/g
const { files } = await workspace.grep(rx, docs, { readText: true })
console.log(`found ${files.length} files`)
/** ------------------------------------------------
* Generate alt text for images
* and update markdown files
*/
// a cache of generated alt text for images
const imgs: Record<string, string> = {}
// process each file
for (const file of files) {
const { filename, content } = file
console.log(`. ${filename}`)
const matches = content.matchAll(rx)
// pre-compute matches
for (const match of matches) {
const url = match[1]
if (imgs[url]) continue // already processed
const resolvedUrl = resolveUrl(filename, url)
console.log(`.. ${resolvedUrl}`)
// execute a LLM query to generate alt text
const { text, error } = await runPrompt(
(_) => {
_.defImages(resolvedUrl)
/**
* TODO: customize the prompt to match your domain
*/
_.$`
You are an expert in assistive technology.
You will analyze each image and generate a description alt text for the image.
- Do not include alt text in the description.
- Keep it short but description.
- Do not generate the [ character.`
},
{
system: [],
maxTokens: 4000,
temperature: 0.5,
cache: "alt-text",
}
)
if (error) throw error
else imgs[url] = text.replace(/\[/g, "") // remove [ from alt text
}
// apply replacements
const newContent = content.replace(
rx,
(m, url) => `![${imgs[url] ?? ""}](${url})`
)
// save updated content
if (newContent !== content) await workspace.writeText(filename, newContent)
}