@wulutech/term-tooltip
v0.1.0
Published
Zero-dependency terminology tooltip plugin for technical content sites. Build-time injection + lightweight runtime.
Maintainers
Readme
@wulutech/term-tooltip
零依赖、构建时注入、运行时增强的术语悬浮卡插件。专为技术创作者站点(如 5loi.com)设计。
✨ 特性
- 🎯 零侵入 — 仅需维护
glossary.yml,构建时自动匹配注入 - ⚡ 极致轻量 — 运行时 < 3KB gzip,零依赖,Tree-shakable
- ♿ 无障碍优先 — 语义化
<dfn>,键盘可访问,原生title降级 - 🎨 主题可定制 — CSS 变量驱动,自适应深/浅色模式
- 🔧 高度可配置 — 支持精确/模糊匹配、自定义触发器、自定义渲染器
- 📦 生态友好 — 支持 Next.js、Astro、Vite、Remix 等任意框架
📦 安装
pnpm add @wulutech/term-tooltip
# 或
npm install @wulutech/term-tooltip
# 或
yarn add @wulutech/term-tooltip🚀 快速开始
1. 准备术语表
创建 content/glossary.yml:
version: 1
terms:
- id: mla
label: MLA
definition: |
**Multi-head Latent Attention (MLA)** 是 DeepSeek-V2 引入的注意力机制创新。
通过低秩压缩将 Key 和 Value 投影到潜在空间,大幅降低 KV Cache 显存占用。
aliases:
- Multi-head Latent Attention
- 多头潜在注意力
tags: [DeepSeek, Attention, KV Cache]
sources:
- type: paper
title: "DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model"
url: "https://arxiv.org/abs/2405.04434"
related: [kvcache, mqa, gqa]
seeAlso: [/lab/deepseek-mla]
- id: kvcache
label: KV Cache
definition: **Key-Value Cache** 是 Transformer 推理时缓存的 Key/Value 投影,随序列长度线性增长。
aliases: [Key-Value Cache, 键值缓存]
tags: [Inference, Memory]
related: [mla, mqa, gqa]2. 构建时集成
Next.js (App Router)
// lib/mdx.ts
import { rehypeTermTooltip } from '@wulutech/term-tooltip/rehype'
export const mdxOptions = {
rehypePlugins: [[rehypeTermTooltip, { glossaryPath: 'content/glossary.yml' }]],
}Astro
// astro.config.mjs
import rehypeTermTooltip from '@wulutech/term-tooltip/rehype'
export default defineConfig({
markdown: {
rehypePlugins: [[rehypeTermTooltip, { glossaryPath: 'content/glossary.yml' }]]
}
})通用 (unified/rehype)
import { unified } from 'unified'
import rehypeParse from 'rehype-parse'
import rehypeStringify from 'rehype-stringify'
import { rehypeTermTooltip } from '@wulutech/term-tooltip/rehype'
const html = await unified()
.use(rehypeParse, { fragment: true })
.use(rehypeTermTooltip, { glossaryPath: 'content/glossary.yml' })
.use(rehypeStringify)
.process('<p>MLA 优化了 KV Cache</p>')3. 生成术语表 JSON
# 单次生成
npx @wulutech/term-tooltip generate --input content/glossary.yml --output public/glossary.json
# 开发模式监听
npx @wulutech/term-tooltip generate --input content/glossary.yml --output public/glossary.json --watch或在 package.json 中:
{
"scripts": {
"generate:glossary": "@wulutech/term-tooltip generate --input content/glossary.yml --output public/glossary.json",
"postinstall": "npm run generate:glossary"
}
}4. 客户端初始化
// app/layout.tsx (Next.js) 或 layouts/BaseLayout.astro
'use client'
import { initTermTooltip } from '@wulutech/term-tooltip/client'
import { useEffect } from 'react'
export function TermTooltipProvider() {
useEffect(() => initTermTooltip({
trigger: 'hover focus',
delay: { show: 150, hide: 100 },
debug: process.env.NODE_ENV === 'development',
}), [])
return null
}⚙️ 配置选项
rehypeTermTooltip (构建时)
| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| glossaryPath | string | 必填 | 术语表 JSON 路径(相对于 process.cwd()) |
| matchMode | 'exact' \| 'fuzzy' | 'exact' | 匹配模式:精确词边界 / 模糊包含 |
| ignoreSelectors | string[] | ['.no-term-tooltip', 'code', 'pre', 'kbd', 'samp'] | 忽略的 CSS 选择器祖先 |
| wrapperTag | 'dfn' \| 'span' \| 'abbr' | 'dfn' | 包装元素标签 |
| classPrefix | string | 'term' | CSS 类名前缀 |
| debug | boolean | false | 控制台输出匹配日志 |
initTermTooltip (运行时)
| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| glossary | GlossaryData | null | 直接传入术语表对象(避免 fetch) |
| container | HTMLElement \| string | document.body | Tooltip 挂载容器 |
| trigger | 'hover' \| 'click' \| 'hover focus' | 'hover focus' | 触发方式,空格分隔 |
| delay | {show: number, hide: number} | {show: 150, hide: 100} | 显示/隐藏延迟 ms |
| render | (term) => HTMLElement \| string | 内置渲染器 | 自定义渲染函数 |
| classPrefix | string | 'term' | CSS 类名前缀 |
| debug | boolean | false | 调试日志 |
🎨 主题定制
通过 CSS 变量覆盖(建议在全局 CSS 中):
:root {
--term-tooltip-bg: #1a1f2e;
--term-tooltip-border: #c9a962;
--term-tooltip-text: #e8e4da;
--term-tooltip-muted: #9ca3af;
--term-tooltip-link: #c9a962;
--term-tooltip-link-hover: #e8d5a3;
--term-tooltip-radius: 6px;
--term-tooltip-shadow: 0 8px 32px rgba(0,0,0,0.4);
--term-tooltip-max-width: 320px;
--term-tooltip-font: inherit;
--term-tooltip-z-index: 9999;
}
/* 触发元素样式 */
.term-tooltip {
border-bottom: 1px dotted var(--term-tooltip-border);
cursor: help;
}
.term-tooltip:hover,
.term-tooltip:focus {
border-bottom-style: solid;
border-bottom-width: 2px;
}📝 术语表 Schema
version: 1 # 必填,版本号
terms: # 必填,术语数组
- id: mla # 必填,唯一标识 (kebab-case)
label: MLA # 必填,显示标题
definition: "..." # 必填,定义(支持 Markdown)
aliases: ["别名1", "别名2"] # 可选,匹配用别名
tags: ["标签1", "标签2"] # 可选,分类标签
sources: # 可选,权威来源
- type: paper # paper | code | article | doc
title: "论文标题"
url: "https://..."
related: ["other-term-id"] # 可选,关联术语 ID
seeAlso: ["/internal/link"] # 可选,本站延伸阅读链接🔌 API 参考
入口导出
import {
rehypeTermTooltip, // 构建时 rehype 插件
initTermTooltip, // 运行时初始化函数
generateGlossary, // CLI/Node 生成术语表
// 类型
GlossaryData,
GlossaryTerm,
RehypeTermTooltipOptions,
TermTooltipOptions,
GenerateOptions,
} from '@wulutech/term-tooltip'
// 子路径导出(Tree-shaking 友好)
import { rehypeTermTooltip } from '@wulutech/term-tooltip/rehype'
import { initTermTooltip } from '@wulutech/term-tooltip/client'
import { generateGlossary } from '@wulutech/term-tooltip/cli'类型定义
interface GlossaryTerm {
id: string
label: string
definition: string
definitionHtml: string
aliases: string[]
tags: string[]
sources: { type: 'paper'|'code'|'article'|'doc'; title: string; url: string }[]
related: string[]
seeAlso: string[]
}
interface GlossaryData {
version: number
generatedAt: string
terms: Record<string, GlossaryTerm>
index: Record<string, string> // 归一化键 -> term id
}🧪 测试
pnpm test # 单次运行
pnpm test:watch # 监听模式📦 发布
# 版本升级
npm version patch|minor|major
# 发布
npm publish📄 许可证
MIT © Partner Studio
🤝 贡献
欢迎 PR!请先阅读 CONTRIBUTING.md。
📚 相关项目
- @wulutech/ask-site — 对话式站内检索 Widget
- @wulutech/space-digest — 空间屏语义摘要生成器
- @wulutech/artwork-manifest — 网页艺术参数化 Schema 生成器
