|
| 1 | +# Icons |
| 2 | + |
| 3 | +The icons system provides Nerd Font glyph resolution with automatic emoji fallback, font detection, interactive installation prompts, and categorized icon definitions for kidd CLIs. |
| 4 | + |
| 5 | +Icons is a sub-export of the `@kidd-cli/core` package (`@kidd-cli/core/icons`), not a separate package. It ships as middleware that decorates `ctx.icons` with methods for resolving icon names to glyphs, checking font availability, and triggering installation. |
| 6 | + |
| 7 | +## Key Concepts |
| 8 | + |
| 9 | +### Nerd Font vs Emoji Fallback |
| 10 | + |
| 11 | +Every icon has two representations: a Nerd Font glyph and an emoji fallback. When the middleware initializes, it detects whether Nerd Fonts are installed on the system. All subsequent `ctx.icons.get()` calls resolve to the appropriate variant automatically. |
| 12 | + |
| 13 | +- **Nerd Fonts detected** -- returns the Nerd Font glyph (e.g. `\uE725` for `branch`) |
| 14 | +- **Nerd Fonts not detected** -- returns the emoji fallback (e.g. the twisted arrows emoji for `branch`) |
| 15 | + |
| 16 | +This means commands never need to check font availability themselves. Call `ctx.icons.get('branch')` and the correct character is returned. |
| 17 | + |
| 18 | +### Icon Categories |
| 19 | + |
| 20 | +Icons are organized into four categories: |
| 21 | + |
| 22 | +| Category | Description | Examples | |
| 23 | +| -------- | -------------------------- | -------------------------------------------- | |
| 24 | +| `git` | Version control operations | `branch`, `commit`, `merge`, `pr`, `tag` | |
| 25 | +| `devops` | Infrastructure and CI/CD | `deploy`, `docker`, `kubernetes`, `terminal` | |
| 26 | +| `status` | Status indicators | `success`, `error`, `warning`, `pending` | |
| 27 | +| `files` | File types and filesystem | `file`, `folder`, `typescript`, `json` | |
| 28 | + |
| 29 | +### Auto-Setup |
| 30 | + |
| 31 | +When `autoSetup` is enabled, the middleware checks for Nerd Font availability during initialization. If no Nerd Font is detected, it prompts the user to install one interactively. This runs once at startup, before any command handler executes. |
| 32 | + |
| 33 | +## Adding the Middleware |
| 34 | + |
| 35 | +```ts |
| 36 | +import { cli } from '@kidd-cli/core' |
| 37 | +import { icons } from '@kidd-cli/core/icons' |
| 38 | + |
| 39 | +cli({ |
| 40 | + name: 'my-app', |
| 41 | + version: '1.0.0', |
| 42 | + middleware: [icons()], |
| 43 | + commands: `${import.meta.dirname}/commands`, |
| 44 | +}) |
| 45 | +``` |
| 46 | + |
| 47 | +### With Configuration |
| 48 | + |
| 49 | +```ts |
| 50 | +icons({ |
| 51 | + autoSetup: true, |
| 52 | + font: 'FiraCode', |
| 53 | +}) |
| 54 | +``` |
| 55 | + |
| 56 | +## IconsOptions |
| 57 | + |
| 58 | +| Option | Type | Default | Description | |
| 59 | +| ------------ | -------------------------------- | --------------------- | --------------------------------------------------------------------------- | |
| 60 | +| `icons` | `Record<string, IconDefinition>` | Built-in defaults | Custom icon definitions to merge with defaults | |
| 61 | +| `autoSetup` | `boolean` | `false` | Prompt to install Nerd Fonts if not detected | |
| 62 | +| `font` | `string` | Interactive selection | The Nerd Font family to install (when omitted, shows an interactive picker) | |
| 63 | +| `forceSetup` | `boolean` | `false` | Always show the install prompt, even if fonts are detected | |
| 64 | + |
| 65 | +## IconsContext |
| 66 | + |
| 67 | +The icons middleware decorates `ctx.icons` with an `IconsContext` object providing methods for icon resolution. |
| 68 | + |
| 69 | +| Method | Type | Description | |
| 70 | +| --------------- | ----------------------------------------------- | --------------------------------------------- | |
| 71 | +| `get(name)` | `(name: string) => string` | Resolve an icon name to its glyph string | |
| 72 | +| `has(name)` | `(name: string) => boolean` | Check whether an icon name is defined | |
| 73 | +| `installed()` | `() => boolean` | Whether Nerd Fonts are detected on the system | |
| 74 | +| `setup()` | `() => AsyncResult<boolean, IconsError>` | Interactively prompt to install Nerd Fonts | |
| 75 | +| `category(cat)` | `(cat: IconCategory) => Record<string, string>` | Get all resolved icons for a given category | |
| 76 | + |
| 77 | +### `ctx.icons.get()` |
| 78 | + |
| 79 | +Resolve an icon name to its display string. Returns the Nerd Font glyph when fonts are installed, the emoji fallback otherwise. Returns an empty string when the name is not found. |
| 80 | + |
| 81 | +```ts |
| 82 | +export default command({ |
| 83 | + async handler(ctx) { |
| 84 | + const icon = ctx.icons.get('branch') |
| 85 | + ctx.logger.info(`${icon} Current branch: main`) |
| 86 | + }, |
| 87 | +}) |
| 88 | +``` |
| 89 | + |
| 90 | +### `ctx.icons.has()` |
| 91 | + |
| 92 | +Check whether an icon name exists in the definitions (built-in or custom). |
| 93 | + |
| 94 | +```ts |
| 95 | +if (ctx.icons.has('deploy')) { |
| 96 | + ctx.logger.info(`${ctx.icons.get('deploy')} Deploying...`) |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +### `ctx.icons.category()` |
| 101 | + |
| 102 | +Retrieve all resolved icons for a category as a record of name-to-glyph mappings. |
| 103 | + |
| 104 | +```ts |
| 105 | +const statusIcons = ctx.icons.category('status') |
| 106 | +// { success: '...', error: '...', warning: '...', ... } |
| 107 | + |
| 108 | +ctx.logger.info(`${statusIcons.success} Build passed`) |
| 109 | +ctx.logger.error(`${statusIcons.error} Tests failed`) |
| 110 | +``` |
| 111 | + |
| 112 | +### `ctx.icons.installed()` |
| 113 | + |
| 114 | +Check whether Nerd Fonts are available. When `forceSetup` is enabled, this always returns `false` to allow re-triggering the setup flow. |
| 115 | + |
| 116 | +```ts |
| 117 | +if (!ctx.icons.installed()) { |
| 118 | + ctx.logger.warn('Nerd Fonts not detected. Icons will use emoji fallback.') |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +### `ctx.icons.setup()` |
| 123 | + |
| 124 | +Interactively prompt the user to install Nerd Fonts. Returns a Result tuple with `true` on success or an `IconsError` on failure. On success, subsequent `get()` calls resolve to Nerd Font glyphs. |
| 125 | + |
| 126 | +```ts |
| 127 | +if (!ctx.icons.installed()) { |
| 128 | + const [error, result] = await ctx.icons.setup() |
| 129 | + if (error) { |
| 130 | + ctx.logger.warn(`Font install failed: ${error.message}`) |
| 131 | + } |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +### IconsError |
| 136 | + |
| 137 | +| `type` | Description | |
| 138 | +| -------------------- | -------------------------------- | |
| 139 | +| `'detection_failed'` | Nerd Font detection check failed | |
| 140 | +| `'install_failed'` | Font installation failed | |
| 141 | + |
| 142 | +## Custom Icons |
| 143 | + |
| 144 | +Merge custom icon definitions with the built-in defaults by passing an `icons` record. Each entry must provide both a `nerdFont` glyph and an `emoji` fallback. |
| 145 | + |
| 146 | +```ts |
| 147 | +icons({ |
| 148 | + icons: { |
| 149 | + lambda: { nerdFont: '\uE7A4', emoji: '\u{03BB}' }, |
| 150 | + rust: { nerdFont: '\uE7A8', emoji: '\u{1F980}' }, |
| 151 | + }, |
| 152 | +}) |
| 153 | +``` |
| 154 | + |
| 155 | +Custom definitions override built-in icons with the same name. Access them the same way: |
| 156 | + |
| 157 | +```ts |
| 158 | +const icon = ctx.icons.get('lambda') |
| 159 | +``` |
| 160 | + |
| 161 | +### IconDefinition |
| 162 | + |
| 163 | +```ts |
| 164 | +interface IconDefinition { |
| 165 | + readonly nerdFont: string |
| 166 | + readonly emoji: string |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +## Built-in Icons |
| 171 | + |
| 172 | +### Git |
| 173 | + |
| 174 | +`branch`, `clone`, `commit`, `compare`, `fetch`, `fork`, `git`, `merge`, `pr`, `tag`, `worktree` |
| 175 | + |
| 176 | +### DevOps |
| 177 | + |
| 178 | +`ci`, `cloud`, `deploy`, `docker`, `kubernetes`, `server`, `terminal` |
| 179 | + |
| 180 | +### Status |
| 181 | + |
| 182 | +`error`, `info`, `pending`, `running`, `stopped`, `success`, `warning` |
| 183 | + |
| 184 | +### Files |
| 185 | + |
| 186 | +`config`, `file`, `folder`, `javascript`, `json`, `lock`, `markdown`, `typescript` |
| 187 | + |
| 188 | +## References |
| 189 | + |
| 190 | +- [kidd API Reference](../reference/kidd.md) |
| 191 | +- [Context](./context.md) |
| 192 | +- [Lifecycle](./lifecycle.md) |
0 commit comments