* 在 XDG 图标主题中查找图标文件路径 * 如果找不到则返回 null
(iconName: string)
| 125 | * 如果找不到则返回 null |
| 126 | */ |
| 127 | async function findIconPath(iconName: string): Promise<string | null> { |
| 128 | // 如果是绝对路径且存在,直接返回 |
| 129 | if (iconName.startsWith('/')) { |
| 130 | try { |
| 131 | await fs.access(iconName) |
| 132 | return iconName |
| 133 | } catch { |
| 134 | // 忽略 |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // 去除扩展名(.desktop 文件中有时会带扩展名) |
| 139 | const baseName = iconName.replace(/\.(png|svg|xpm)$/, '') |
| 140 | |
| 141 | const searchPaths = getIconSearchPaths() |
| 142 | |
| 143 | for (const searchPath of searchPaths) { |
| 144 | // 先检查各个主题目录下的常用尺寸 |
| 145 | try { |
| 146 | const entries = await fs.readdir(searchPath, { withFileTypes: true }) |
| 147 | const themes = entries.filter((e) => e.isDirectory()).map((e) => e.name) |
| 148 | for (const theme of ['hicolor', ...themes]) { |
| 149 | for (const size of ICON_PREFERRED_SIZES) { |
| 150 | for (const category of ['apps', 'applications']) { |
| 151 | for (const ext of ICON_EXTENSIONS) { |
| 152 | const iconPath = path.join(searchPath, theme, size, category, baseName + ext) |
| 153 | try { |
| 154 | await fs.access(iconPath) |
| 155 | return iconPath |
| 156 | } catch { |
| 157 | // 忽略 |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | } catch { |
| 164 | // 目录不存在,跳过 |
| 165 | } |
| 166 | |
| 167 | // pixmaps 目录直接查找 |
| 168 | for (const ext of ICON_EXTENSIONS) { |
| 169 | const iconPath = path.join(searchPath, baseName + ext) |
| 170 | try { |
| 171 | await fs.access(iconPath) |
| 172 | return iconPath |
| 173 | } catch { |
| 174 | // 忽略 |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | return null |
| 180 | } |
| 181 | |
| 182 | // ============================================================ |
| 183 | // 拼音首字母支持 |
no test coverage detected