Works out once whether a Nerd Font is installed, with a setting to force it on or off, so mods that draw Nerd Font glyphs know when to fall back

A Claude Code mod that other mods depend on to know whether they can draw Nerd Font icons. It works out the answer once a session, so every mod on screen draws the same way: attention-is-all-i-need's magnifying glass and copy-code's copy label either both use the icon or both fall back to plain characters.
The mod API can't tell which font the terminal draws with, so the mod asks fontconfig instead. If any installed family that fc-list : family lists has "Nerd Font" in its name, the answer is yes. If there's no such family, or no fc-list, the answer is no.
A Nerd Font being installed doesn't mean your terminal uses it. If the guess is wrong, use the setting.
In /config, the Nerd Font glyphs row picks one of:
| Value | Icons are drawn |
|---|---|
auto (default) | when fc-list finds a Nerd Font |
on | always |
off | never |
A change there reloads the mod with the new value, and the mods that read the answer while drawing redraw. The value is kept in settings.json under pluginConfigs.
List nerd-font under dependencies in your plugin.json:
{ "dependencies": ["nerd-font"] }
Read the answer from $.state while drawing. Your drawing then redraws when the answer arrives or changes:
const nerdFont = { plugin: 'nerd-font', key: 'enabled' } as const
on('ui.render', { component: 'AbovePrompt', surface: 'terminal' }, async ($, e) => {
const { value: hasNerdFont } = await $.state.get(nerdFont)
const { Text } = $.ui.resolve(e)
return <Text>{hasNerdFont ? '\u{F0349}' : '🔍'}</Text>
})
The value is undefined until the session's check has run, so treat anything but true as no. Claude Code lays this mod's contract into your mod's .claude-plugin/types/nerd-font/index.d.ts, so the key and its type are checked.
/plugin install nerd-font --marketplace AH-Merii/claude-pluginshooks/register.ts 38 lines1import type { EngineInterface as Engine, Register } from 'claude-code'
2
3const enabled = { plugin: 'nerd-font', key: 'enabled' } as const
4
5// Whether fontconfig lists a font family with "Nerd Font" in its name. The mod API can't tell which font the
6// terminal draws with, so an installed one is the best guess; the setting covers a terminal that doesn't use it.
7async function installed($: Engine) {
8 try {
9 const { stdout } = await $.process.run(['fc-list', ':', 'family'], { timeoutMs: 5000 })
10 return stdout.includes('Nerd Font')
11 } catch {
12 // No fc-list on this machine, or no processes on this surface: glyphs would show as empty boxes.
13 return false
14 }
15}
16
17export const register: Register = (on, options) => {
18 // The answer as last worked out; fonts don't change within a process, so it is checked once per load.
19 let answer: boolean | undefined
20
21 // Once a session, and again when the module reloads, as it does when the setting changes in /config.
22 on('session.start', async ($, e, next) => {
23 answer = options.glyphs === 'on' || (options.glyphs !== 'off' && (await installed($)))
24 await $.state.set(enabled, answer)
25 return next(e)
26 })
27
28 // A /clear starts a new session whose state starts empty, and no session.start fires for it; the settings-style
29 // SessionStart does, with source "clear", inside the new session.
30 on('classic.SessionStart', async ($, e, next) => {
31 const result = await next(e)
32 if (e.source === 'clear' && answer !== undefined) {
33 await $.state.set(enabled, answer)
34 }
35 return result
36 })
37}
38types/index.d.ts 8 lines1declare module 'claude-code' {
2 interface PluginState {
3 // Whether mods should draw Nerd Font glyphs: the user's setting, or else whether an installed font's family
4 // has "Nerd Font" in its name. Undefined until the session's check has run.
5 'nerd-font': { enabled: boolean }
6 }
7}
8