大佬的扩展做的非常实用,但是“技术”这栏过于详细不能点开一目了然,技术栈较多的情况需要下拉很长才能看全
我稍微改了一下,加了一栏“技术栈”(随便命名的),可以直接显示所有技术没有详细来源和信任理由,会显示技术栈图标(如果有),图标来自扩展Wap*****,似乎是有5k+,包含PNG和SVG。因为没看见大佬有未来开发计划,代码索性就不提交了,未来如有计划随时恭候。
图标加载策略
- 优先: public/skills/ 本地文件 (通过 skills-index.json 查找)
- 兜底: cdn.simpleicons.org/{slug}
- 无图标: 直接显示技术栈,不占位
public/skills/
| 索引 |
文件名 |
react |
React.svg |
reactrouter |
React Router.svg |
tencentcloud |
Tencent Cloud.svg |
vue |
vue.svg |
skills-index.json 索引生成
import { readdirSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const root = resolve(__dirname, '..')
const skillsDir = resolve(root, 'public/skills')
const outputPath = resolve(root, 'public/skills-index.json')
console.log('[build-skills-index] scanning skills directory...')
let files
try {
files = readdirSync(skillsDir)
} catch {
console.error('[build-skills-index] skills directory not found, skipping')
process.exit(0)
}
const normalize = (name) =>
String(name ?? '')
.toLowerCase()
.replace(/&/g, 'and')
.replace(/\+/g, 'plus')
.replace(/[^a-z0-9一-龥]+/g, '')
const index = {}
for (const file of files) {
if (file.startsWith('.')) continue
const extIndex = file.lastIndexOf('.')
if (extIndex === -1) continue
const ext = file.slice(extIndex + 1).toLowerCase()
if (ext !== 'svg' && ext !== 'png') continue
const name = file.slice(0, extIndex)
const normalized = normalize(name)
// 两个都有优先选SVG
if (!index[normalized] || (ext === 'svg' && index[normalized].endsWith('.png'))) {
index[normalized] = file
}
}
const json = { schemaVersion: 1, skillsIndex: index }
writeFileSync(outputPath, JSON.stringify(json))
console.log(`[build-skills-index] wrote ${outputPath} with ${Object.keys(index).length} entries`)

大佬的扩展做的非常实用,但是“技术”这栏过于详细不能点开一目了然,技术栈较多的情况需要下拉很长才能看全
我稍微改了一下,加了一栏“技术栈”(随便命名的),可以直接显示所有技术没有详细来源和信任理由,会显示技术栈图标(如果有),图标来自扩展Wap*****,似乎是有5k+,包含PNG和SVG。因为没看见大佬有未来开发计划,代码索性就不提交了,未来如有计划随时恭候。
图标加载策略
public/skills/
reactreactroutertencentcloudvueskills-index.json 索引生成