逐文件精读源码生成。行号基于当前工作区版本,仅用于快速定位。 覆盖
src/components/ui/全部 38 个文件(新增radio-group.tsx、game-badge.tsx) +src/components/下 3 个基础文件(共 41 个),以及src/components/crash-report-host.tsx崩溃提示宿主。
职责:账户头像组件,图片加载失败时回退为首字母占位符。
导出:AccountAvatar
主要依赖:@/utils/imageLoader(Img 图片组件)、clsx
| 符号 | 位置 | 说明 |
|---|---|---|
SIZE_CLASS |
account-avatar.tsx:11 | size → 宽高/字号映射(xs 32px ~ xl 80px) |
AccountAvatar |
account-avatar.tsx:19 | 主组件。关键 props:src(可选图片地址)、alt(必填,兼作回退首字母来源)、size(xs/sm/md/lg/xl,默认 md)、className |
备注:onError 时隐藏图片并把父节点文本设为 alt 首字母大写(account-avatar.tsx:40-45);图片类名固定带 avatar-feather。原先的 showActiveIndicator(右下角绿色激活圆点)已随「切换账户」弹窗改用行尾 ACTIVE 徽章表达选中态而移除。
职责:行内警示条,按 status 切换边框与指示点配色,复合组件结构(容器 + 指示点 + 内容 + 描述)。
导出:Alert、GlassAlert(Alert 的别名)、类型 GlassAlertStatus、GlassAlertProps
主要依赖:react(createContext/useContext)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassAlertStatus |
alert.tsx:4 | 类型:default / success / warning / danger |
GlassAlertProps |
alert.tsx:6 | 继承 HTMLAttributes<HTMLDivElement>,新增 status |
AlertCtx |
alert.tsx:10 | 内部 Context,向子件传递 status |
borderTone / dotTone |
alert.tsx:12 / alert.tsx:19 | status → 边框文字色 / 圆点色映射 |
Alert |
alert.tsx:26 | 根容器,role="alert",毛玻璃背景;props:status(默认 default)、className、children |
Alert.Indicator |
alert.tsx:43 | 8px 圆点,颜色跟随 Context status;props:className |
Alert.Content |
alert.tsx:48 | 可伸缩内容区(min-w-0 flex-1) |
Alert.Description |
alert.tsx:52 | text-sm 段落文本 |
GlassAlert |
alert.tsx:60 | Alert 的别名导出 |
备注:子件通过 Context 自动继承 status,Indicator 无需再传色。
职责:项目级图标注册表——把 lucide 图标包成带动效的 MorphIcon 组件;同时含一个独立的 StatusDot 实现。
导出:17 个图标组件 + StatusDot
主要依赖:lucide(注意包名是 lucide 而非 lucide-react)、@/components/morph-icon(createMorphIcon)、clsx
| 符号 | 位置 | 说明 |
|---|---|---|
RefreshIcon |
app-icon.tsx:23 | 刷新(RefreshCw) |
ChevronLeftIcon |
app-icon.tsx:24 | 左箭头 |
ChevronRightIcon |
app-icon.tsx:25 | 右箭头 |
ChevronDownIcon |
app-icon.tsx:26 | 下箭头(select.tsx 用作下拉指示) |
CloseIcon |
app-icon.tsx:27 | 关闭(X) |
PlusIcon |
app-icon.tsx:28 | 加号 |
EditIcon |
app-icon.tsx:29 | 编辑(Pencil) |
CheckIcon |
app-icon.tsx:30 | 勾选 |
SwitchIcon |
app-icon.tsx:31 | 切换(ArrowLeftRight) |
MinimizeIcon |
app-icon.tsx:32 | 最小化(Minus) |
MaximizeIcon |
app-icon.tsx:33 | 最大化(Square) |
RestoreIcon |
app-icon.tsx:34 | 还原(Copy) |
MenuIcon |
app-icon.tsx:35 | 菜单(汉堡) |
BellIcon |
app-icon.tsx:36 | 铃铛 |
LinkIcon |
app-icon.tsx:37 | 链接(Link2) |
UnlinkIcon |
app-icon.tsx:38 | 断开链接 |
InfoIcon |
app-icon.tsx:39 | 信息 |
GlobeIcon |
app-icon.tsx:40 | 地球(网络/从系统读取代理) |
LoadingIcon |
app-icon.tsx:41 | 加载圆环(LoaderCircle,配 animate-spin 使用) |
StatusDot |
app-icon.tsx:43 | 状态圆点。props:tone(success/warning/danger/default)、pulse(默认 false,animate-ping 光晕)、size(sm 8px / md 12px / lg 16px,默认 md)、className |
备注:与 status-dot.tsx 的 StatusDot 同名但 API 不同(此处支持 size 用 pulse,彼处固定 12px 用 ping),import 时注意路径。
职责:滚动容器的回到顶部悬浮球(Portal 到 body),默认显示滚动百分比,悬停切换为上箭头。
导出:BackToTopFab
主要依赖:react-dom(createPortal)、@/lib/cn、morphicons/react(MorphIcon)、lucide(ArrowUp)
| 符号 | 位置 | 说明 |
|---|---|---|
BackToTopFab |
back-to-top.tsx:11 | props:getContainer(返回滚动容器的函数)、className。滚动超过 20% 显示(back-to-top.tsx:32);点击平滑回顶(back-to-top.tsx:61) |
备注:用 requestAnimationFrame 轮询等待容器挂载(back-to-top.tsx:34-44);固定定位 bottom-6 right-6 z-[10003];文件头注释写"超过 50% 显示",实际代码是 p > 20,以代码为准。
职责:胶囊形标签(Chip/Badge),三维度变体:填充样式 x 尺寸 x 语义色。
导出:Badge、GlassChip(别名)、类型 GlassChipTone、GlassChipProps
主要依赖:class-variance-authority(cva)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassChipTone |
badge.tsx:4 | 语义色:default/primary/success/warning/danger/accent |
GlassChipProps |
badge.tsx:6 | 继承 HTMLAttributes<HTMLSpanElement> + cva VariantProps;variant(soft/solid/outline,默认 soft)、size(sm/md/lg,默认 md)、color(默认 default) |
badgeVariants |
badge.tsx:14 | cva 定义:基础类(inline-flex rounded-full transition-all)+ size 尺寸(sm h-5 / md h-6 / lg h-8) |
softTone / solidTone / outlineTone |
badge.tsx:45 / 54 / 63 | color → 三套填充/描边配色表 |
Badge |
badge.tsx:72 | 按 variant 选取配色表并合并类名渲染 <span> |
GlassChip |
badge.tsx:97 | Badge 别名(status-badge.tsx 经 glass 桶引用它) |
职责:按钮组件,提供基础版 Button 与增强版 GlassButton(loading / 图标-only / 前后置内容)。
导出:Button、GlassButton、buttonVariants、类型 GlassButtonVariant、GlassButtonSize、GlassButtonProps、ButtonProps
主要依赖:react(forwardRef)、class-variance-authority、@/lib/utils、./spinner(GlassSpinner)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassButtonVariant |
button.tsx:6 | primary/secondary/tertiary/outline/ghost/danger |
GlassButtonSize |
button.tsx:7 | sm/md/lg |
GlassButtonProps |
button.tsx:9 | 关键 props:variant(默认 secondary)、size(默认 md)、isIconOnly(方形图标按钮)、isDisabled、isLoading(前置 spinner 并禁用)、fullWidth、onPress(点击回调)、startContent/endContent(前后插槽节点) |
buttonVariants |
button.tsx:22 | cva 变体表;基础类含 hover scale-105、active scale-95、focus ring、disabled 50% 透明。lg-control 玻璃底挂在 primary/secondary/outline/danger 四个变体上(不含 ghost/tertiary)——历史上挂在基础类 + 用 before:hidden 隐藏 ghost/tertiary 的玻璃层,结果 display:none 连带杀死调用方经 className 追加的 lg-material(同属宿主的 ::before 一票否决),导致账户胶囊等 ghost+lg-material 组合丢失玻璃底。primary/danger 另加 lg-solid:实底按钮的 ::before 只留边缘高光、不铺玻璃填充,否则深色主题下 --primary 被 tint 压成灰紫,而前景按 --primary-foreground(indigo 上算出的近黑 #0b0b0d)落上去几乎读不出;顶光引用 --lg-edge-glint(柔光令牌,带 1px blur),不写死白色 rgba。secondary/outline 的 hover/按压提亮改走 lg-glass-interactive(:hover/:active 只给 ::before 加 filter: brightness()),已不再写 hover:brightness-105 / active:brightness-95——宿主一旦带 filter 就成为 backdrop root,按钮一悬停便从毛玻璃退化成半透明色块(见 types_locales_styles.md 的「backdrop root 约束」);primary/danger 的 hover:bg-*/90 只改底色,不受此限 |
iconOnlySize |
button.tsx:55 | isIconOnly 时 sm=32 / md=36 / lg=40 的方形尺寸 |
ButtonProps |
button.tsx:61 | 基础按钮 props(原生 button attrs + variant/size) |
Button |
button.tsx:65 | forwardRef;type 默认 "button",variant 默认 secondary、size 默认 md |
GlassButton |
button.tsx:96 | forwardRef;按下态 = disabled 或 isDisabled 或 isLoading;渲染顺序 spinner → startContent → children → endContent(button.tsx:132-135) |
备注:GlassButtonProps 与 ButtonProps 是两套 props 不可混用(前者用 onPress/isXxx 命名,后者是原生属性)。
职责:卡片容器族——可按压卡片 GlassCard、静态卡片 Card 及其标题/描述/内容/页脚子件。
导出:Card、CardHeader、CardTitle、CardDescription、CardContent、CardFooter、GlassCard
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassCardProps |
card.tsx:3 | isPressable(渲染为 <button>)、onPress(点击回调)、shadow(none/sm/md/lg,默认 none) |
GlassCard |
card.tsx:9 | 毛玻璃卡(glass-surface rounded-xl border);isPressable=true 时输出 <button> 并带 hover 抬升缩放动效(card.tsx:17-38),否则输出 <div> |
CardProps |
card.tsx:58 | 仅 shadow + 原生 div attrs |
Card |
card.tsx:62 | 静态毛玻璃卡(无 hover 位移动效) |
CardHeader |
card.tsx:79 | 头部容器 p-6 space-y-1.5 |
CardTitle |
card.tsx:91 | <h3> 标题,text-lg font-semibold |
CardDescription |
card.tsx:109 | <p> 描述,text-sm text-muted |
CardContent |
card.tsx:121 | 内容区 p-6 pt-0 |
CardFooter |
card.tsx:133 | 页脚 flex items-center p-6 pt-0 |
职责:自绘复选框(原生 input sr-only + 视觉 span),两套 API:GlassCheckbox 与 Checkbox。
导出:Checkbox、GlassCheckbox
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassCheckboxProps |
checkbox.tsx:3 | 原生 input attrs(省略 checked/onChange)+ isSelected(选中态)、onValueChange(selected)、onChange()(无参,先于 onValueChange 触发) |
GlassCheckbox |
checkbox.tsx:10 | 选中时主色底 + 内联 SVG 勾(checkbox.tsx:41-49);children 作为 label 文本 |
CheckboxProps |
checkbox.tsx:57 | checked、onCheckedChange(checked) |
Checkbox |
checkbox.tsx:63 | 视觉与 GlassCheckbox 相同,仅回调 API 不同 |
备注:两者都是受控组件(不传状态则始终未选中);视觉方块固定 18x18。
职责:以 Promise 形式替代 window.confirm 的编程式确认弹窗(Host + 触发函数),另含开发者警告弹窗。
导出:confirmDialog、ConfirmDialogHost、DevWarningDialog
主要依赖:@/components/ui/glass(GlassAlertDialog、GlassButton、GlassModal)
| 符号 | 位置 | 说明 |
|---|---|---|
ConfirmTone / ConfirmOptions |
confirm-dialog.tsx:4 / 6 | tone:primary/danger/warning;选项含 title、body、confirmText(默认 "Confirm")、cancelText(默认 "Cancel") |
externalHandler |
confirm-dialog.tsx:14 | 模块级 handler 槽,Host 挂载时注入 |
confirmDialog |
confirm-dialog.tsx:17 | 任意位置调用,返回 Promise<boolean>;未挂载 Host 时静默 resolve(false) |
ConfirmDialogHost |
confirm-dialog.tsx:22 | 单例宿主:注册 handler(confirm-dialog.tsx:39-44)、渲染 GlassModal 复合结构;tone 为 danger 时确认键变 danger 变体(confirm-dialog.tsx:54) |
DevWarningDialog |
confirm-dialog.tsx:94 | 受控警告框(isOpen/onOpenChange/onConfirm/title/body/confirmText/cancelText),基于 GlassAlertDialog |
备注:必须在应用树中挂载一次 ConfirmDialogHost,否则 confirmDialog() 恒返回 false。
职责:平滑递增的时长显示(h:mm:ss)。在后端快照(每 5 秒)之间按真实流逝时间推进,并按位播放滚动/淡入动画,避免数字整秒硬跳与快照回溯抖动。
导出:ElapsedTime、ElapsedTimeProps
主要依赖:react(useEffect/useRef/useState)、@/lib/utils(cn);样式类 .elapsed-digit / .elapsed-sep(定义在 src/styles/globals.css)
| 符号 | 位置 | 说明 |
|---|---|---|
ElapsedTimeProps |
elapsed-time.tsx:6 | totalSeconds(权威总秒数:已累计 + 当前会话)、running(为 true 时按真实流逝时间平滑推进)、className |
TimeParts / toParts |
elapsed-time.tsx:15 / :22 | 把整秒拆成 hours/minutes(两位)/tens/ones 四个字符串位 |
ElapsedTime |
elapsed-time.tsx:48 | rAF 循环按 Date.now() 推算秒数,仅在跨越整秒时更新 state(避免每帧重渲染);totalSeconds/running 变化时以已展示秒数为下限重新锚定,保证数字只前进不回退;sr-only 文本提供完整时可读值,视觉部分 aria-hidden |
备注:prefers-reduced-motion 下无位动画,仅逐秒切换。被 src/components/game-action-panel.tsx 的累计游戏时长徽章使用。
职责:空状态占位(图标 + 标题 + 描述 + 操作按钮),内置不依赖外部库的用户图标 SVG。
导出:EmptyState、EmptyStateUserIcon
主要依赖:clsx
| 符号 | 位置 | 说明 |
|---|---|---|
EmptyStateProps |
empty-state.tsx:3 | icon、title(必填)、description、action(按钮节点)、minHeight(默认 200)、className |
EmptyState |
empty-state.tsx:14 | 垂直居中容器;图标固定渲染 56px、40% 透明、hover 放大(empty-state.tsx:31) |
EmptyStateUserIcon |
empty-state.tsx:47 | 24x24 用户轮廓 SVG,接受全部 SVGProps;被 error-boundary.tsx、global-error-fallback.tsx 复用 |
职责:类组件错误边界,捕获子树未捕获异常,写入崩溃日志并展示可重置的兜底 UI。
导出:ErrorBoundary
主要依赖:react(Component)、react-i18next(默认兜底文案)、@/components/ui/glass(GlassButton)、./empty-state(EmptyStateUserIcon)、@/utils/crashReporting(reportCrash)、@/i18n
| 符号 | 位置 | 说明 |
|---|---|---|
ErrorFallbackRenderer |
error-boundary.tsx:8 | (error, reset) => ReactNode,fallback 支持节点或渲染函数 |
ErrorBoundaryProps |
error-boundary.tsx:10 | children、fallback(节点/函数)、onError(error, info) |
ErrorBoundary |
error-boundary.tsx:25 | getDerivedStateFromError 置错;componentDidCatch 调 reportCrash({source:"react", fatal:true, stack: error.stack + componentStack}) 并转发 onError;reset 清除错误态 |
DefaultErrorFallback |
error-boundary.tsx:66 | 内部未导出:60vh 居中错误页 + 错误消息 + Reload 按钮调用 onReset |
备注:Provider 传入渲染函数 (error) => <GlobalErrorFallback error={error} />,因此根级错误展示完整诊断面板;默认兜底仅在单独使用 ErrorBoundary 时出现。
职责:游戏标识徽标 + 账号副标题行——区分账户/角色属于《明日方舟:终末地》还是《明日方舟》。 账户页、账户切换弹窗、抽卡/签到/区域/帝江号页、仪表盘、侧边栏当前账号、标签页默认角色、 签到卡片设置、以及 7 个可多账号切换的卡片(account-info / account-progress / achievement / character-list / domain-info / spaceship / attendance-settings-modal)都复用它。
⚠️ 规则:任何列出或选择多个账号的位置,都必须显示游戏标识。 两个游戏的官服角色serverId都是1、服务器名都叫「官服」,只写「官服 · Lv.60」 无法分辨是哪条记录属于哪个游戏。漏显示比多显示危险得多,因此AccountNameLine不提供关闭开关。位置约定:徽标紧跟名字之后、与名字垂直居中,不推到行尾。 原因有二:① 行尾常有选中勾 / ACTIVE 标示,
ml-auto会把徽标挤到它们旁边 (实测截图里就挤在一起了);② 徽标若与名字分处两行,视觉上会"掉下去"、 和名字对不齐。因此徽标始终与名字同排,出现方式统一为<p><AccountNameLine game={...}>{nickname}</AccountNameLine></p>, 「服务器 · 等级」自成一行且不含游戏信息。
导出:GAME_META、normalizeGameId、AccountNameLine、GameBadge
主要依赖:react-i18next(useTranslation,文案走 game.* 命名空间)、clsx、@/utils/accountService 的 GameId 类型
| 符号 | 位置 | 说明 |
|---|---|---|
GAME_META |
game-badge.tsx:14 | Record<GameId, {icon, labelKey, shortKey}>:图标复用 public/games/*.png(与左侧游戏切换栏同一份资源),文案键为 game.endfield / game.endfield_short / game.arknights / game.arknights_short。中文文案约定:终末地短名用 终末地(全称 明日方舟:终末地 太长,徽标放不下);明日方舟短名用全称 明日方舟,不用缩写 方舟 —— 徽标是同屏唯一能区分两作的凭据,缩写会让不熟悉的用户看不懂 |
normalizeGameId(game?) |
game-badge.tsx:32 | 任意字符串 → GameId;"arknights" 之外一律按 endfield(覆盖后端返回 undefined 的老缓存) |
AccountNameLine |
game-badge.tsx:45 | props:game?、children(账号名)、className。inline-flex max-w-full 渲染「名字 + 紧跟其后的游戏徽标」,items-center 保证徽标与文字垂直居中。内层名字带 min-w-0 truncate(flex item 默认 min-width:auto,漏了就截断失效、长昵称会把按钮顶出 max-w-48),徽标 shrink-0。直接放在 <p> / <div> 里即可,不需要外层再写 flex |
GameBadge |
game-badge.tsx:80 | props:game?、variant(icon 只有图标 / short 图标+短名 / full 图标+完整游戏名,默认 short)、size(sm/md)、className(追加在末尾)。title 恒为完整游戏名,便于悬停查看。注:明日方舟的 short 与 full 目前文案相同(都用「明日方舟」),终末地仍是「终末地 / 明日方舟:终末地」 |
备注:
- 图标是
<img>而非内联 SVG:public/games/*.png已按官方启动器的超椭圆切好 alpha,rounded-[5px]只做轻微收敛。 - 项目约定「持久化图标标识而非字面量」——这里存的是
gameid(endfield/arknights),图标路径与文案都由GAME_META查表得出,没有把 emoji 或路径写进配置。 - 游戏文案不放硬编码中文:
game.*命名空间在src/locales/{zh,en}/translation.json各有一份。 - 标准用法(账号列表项、角色下拉、侧边栏、卡片组统一此形状):
行尾若还有选中勾 / ACTIVE 标示,让它留在父级 flex 的末尾(配
<p className="text-sm font-medium"> <AccountNameLine game={acc.game}>{acc.nickname}</AccountNameLine> </p> <p className="text-xs text-muted truncate"> {resolveServerLabel(acc.server, i18n.language)} · Lv.{acc.level} </p>
ml-auto shrink-0),不要和徽标抢位置。 - 折叠态的账号按钮(抽卡页 / 区域页 / 帝江号页的角色切换按钮)同样用
AccountNameLine包裹昵称。
职责:全局顶部浮层提示(toast 式 Alert),模块级发布/订阅,可在任意模块直接调用。
导出:pushGlobalAlert、GlobalAlertHost
主要依赖:@/components/ui/glass(GlassAlert)、react(useEffect/useState)
| 符号 | 位置 | 说明 |
|---|---|---|
GlobalAlertTone / GlobalAlertState |
global-alert.tsx:4 / 6 | tone:success/danger/warning/default;state 含自增 id、message、duration |
listeners |
global-alert.tsx:14 | 模块级 Set 订阅表 |
pushGlobalAlert |
global-alert.tsx:17 | (tone, message, duration=3000),向所有 Host 广播 |
GlobalAlertHost |
global-alert.tsx:32 | 根布局挂载;按 duration 定时清除(带 id 校验避免旧定时器误清新消息,:38-40);渲染为顶部居中固定浮层 z-50 + animate-slide-down |
备注:同屏只显示最后一条 alert(新覆盖旧);z-50 低于 modal 的 z-[100],弹窗打开时提示会被遮挡。
职责:应用根级 ErrorBoundary 的整页兜底(全屏错误页 + 可能原因/建议 + 复制/导出/打开日志/重载/退出)。
导出:GlobalErrorFallback
主要依赖:react(useMemo)、react-i18next、@tauri-apps/api/core(invoke("app_quit"))、@/components/crash-report-host(CrashDetails/CrashActionBar)
| 符号 | 位置 | 说明 |
|---|---|---|
GlobalErrorFallback |
global-error-fallback.tsx:11 | props:error: Error;用 CrashReport 包装后渲染 CrashDetails 与 CrashActionBar(primary=重新加载、危险按钮=退出应用) |
备注:导出诊断日志与打开日志目录由 CrashActionBar 统一实现;该组件位于 Provider 的 ErrorBoundary 外层兜底中,因此不依赖 GlobalAlertHost。
职责:全局崩溃/API 熔断提示宿主(约 356 行)。订阅 crashReporting 的当前报告,在应用内弹出阻塞/非阻塞 Modal,展示错误摘要、可能原因、建议、技术详情,并提供复制、导出诊断日志、打开日志目录、重试/重载/退出操作。
导出:CrashDetails、CrashActionBar、CrashReportHost。私有:useCrashReportState。
主要依赖:react、react-i18next、react-router-dom(useNavigate)、@tauri-apps/api/core(reset_api_health/app_quit)、morphicons/react + lucide 图标(含 UserRound)、@/components/ui/glass(GlassAlert/GlassButton)、@/components/ui/modal(GlassModalCompound)、@/utils/diagnostics、@/utils/crashReporting
| 符号 | 位置 | 说明 |
|---|---|---|
useCrashReportState(私有) |
:25 | 订阅崩溃报告并在挂载时 loadPendingCrashReports() |
CrashDetails |
:45 | 图标 + 摘要 + 时间 + 时钟偏差警示 + 数美设备指纹警示(api.kind === "device_fingerprint" 时用 crash.api_fingerprint_body)+ diagnoseCrash 的「可能原因/建议处理」+ 折叠技术详情(message/detail/stack/API health) |
CrashActionBar |
:129 | 复制报告、导出 zip 并 revealItemInDir、打开日志目录、可选关闭/退出、主操作按钮;底部内联状态文案 |
CrashReportHost |
:253 | 读取当前报告并渲染 GlassModal(z-[150]);fatal 报告禁止背景关闭/Escape;API 报告额外提供「前往账户页」按钮(useNavigate 跳 /account),主按钮先 reset_api_health 再重载;关闭时确认崩溃报告路径 |
备注:挂载于 src/provider.tsx 的 ErrorBoundary 内。API 熔断报告(source:"api")现为非致命:先调用 startupProgress.abortStartup 收起 Splash 让用户可继续进入应用,账户由 accountAvailability 标记为不可用(账户页展示「账号不可用 / 重新登录」),弹窗可关闭;修复后点「已修复,重试」清除后端 BLOCKED 状态并重载。数美设备指纹失败时提示重启电脑并同步时区。i18n 键前缀 crash.*(新增 api_fingerprint_body、cause_fingerprint、suggestion_fingerprint、action_go_account)。
职责:OTP 验证码输入——隐藏真实 input 承担键盘与粘贴,可视格子从 Context 读取字符。
导出:InputOTP、GlassInputOTP(别名,均带 .Group/.Slot/.Separator 子件)
主要依赖:react(createContext/useContext/useRef)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassInputOTPProps |
input-otp.tsx:4 | value、onChange(value)、onComplete(value)(达到 maxLength 时触发)、maxLength(默认 6)、isInvalid(格子红边)、aria-describedby、className、children |
OTPCtx |
input-otp.tsx:15 | 向 Slot 传递 { value, isInvalid } |
InputOTP |
input-otp.tsx:20 | 隐藏 input(inputMode="numeric" autoComplete="one-time-code",:37-50),输入过滤为 [0-9A-Za-z];点击容器聚焦(:35) |
InputOTP.Group |
input-otp.tsx:56 | 格子横排容器(flex gap-2) |
InputOTP.Slot |
input-otp.tsx:66 | 单格:props index、className;渲染 value[index],isInvalid 时 danger 边框;固定 36x44 |
InputOTP.Separator |
input-otp.tsx:89 | "—" 分隔符 |
GlassInputOTP |
input-otp.tsx:99 | 同一函数的别名并重复挂载三个子件 |
备注:InputOTP 与 GlassInputOTP 行为完全一致。
职责:文本输入框,含增强版 GlassInput(尺寸/状态/前后插槽)与精简版 Input。
导出:Input、GlassInput
主要依赖:react(forwardRef)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassInputProps |
input.tsx:4 | variant(声明但未用于样式)、size(sm h-8 / md h-9 / lg h-11,默认 md)、isDisabled、isInvalid(danger 边框)、isClearable(声明但未实现)、startContent/endContent(绝对定位插槽,pointer-events-none)、onValueChange(value) |
GlassInput |
input.tsx:16 | 有插槽时外包 relative 容器并加 pl-9/pr-9(:52-53, :62-72);同时触发原生 onChange 与 onValueChange |
InputProps / Input |
input.tsx:78 / 81 | 原生 input 直通版,type 默认 text,固定 h-9 rounded-xl |
备注:variant 与 isClearable 目前是死 props(未参与渲染逻辑)。
职责:键盘按键标签 <kbd>,提供键名到符号的缩写映射。
导出:Kbd、GlassKbd(别名,带 .Abbr/.Content 子件)
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassKbdProps |
kbd.tsx:3 | 继承 HTMLAttributes<HTMLElement> |
KEY_SYMBOLS |
kbd.tsx:5 | 键名映射:command→⌘、control→⌃、option→⌥、shift→⇧、enter→↵、backspace→⌫、space→␣、up/down/left/right→箭头 |
Kbd |
kbd.tsx:19 | <kbd> 样式容器(10px 字号、边框底色) |
Kbd.Abbr |
kbd.tsx:34 | props:keyValue、className;title 保留原始键名,显示符号(无映射则原样显示) |
Kbd.Content |
kbd.tsx:45 | 纯文本包裹 span |
职责:表单标签 <label> 的统一样式。
导出:Label、GlassLabel
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassLabelProps |
label.tsx:3 | 继承 LabelHTMLAttributes<HTMLLabelElement> |
Label |
label.tsx:5 | text-sm font-medium text-foreground;GlassLabel(:13)为别名 |
职责:链接 <a> 的基础样式(hover 变主色并轻微上浮)。
导出:Link、GlassLink
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassLinkProps |
link.tsx:3 | 继承 AnchorHTMLAttributes<HTMLAnchorElement> |
Link |
link.tsx:5 | inline-flex + hover:text-primary hover:-translate-y-0.5;GlassLink(:21)为别名 |
职责:三种加载占位——居中圆环大块、行内小 spinner、多行骨架列表。
导出:LoadingBlock、LoadingInline、SkeletonList
主要依赖:@/components/ui/glass(GlassCard、GlassProgressCircle、GlassSpinner)、clsx
| 符号 | 位置 | 说明 |
|---|---|---|
LoadingBlockProps / LoadingBlock |
loading-block.tsx:4 / 13 | label(环下方文案)、minHeight(默认 200)、className;role="status",内含不定量圆环(GlassProgressCircle isIndeterminate lg) |
LoadingInline |
loading-block.tsx:39 | label?;sm 号 current 色 spinner + 文案,用于按钮/小区域 |
SkeletonListProps / SkeletonList |
loading-block.tsx:49 / 58 | count(行数)、rowHeight(默认 80)、gap(默认 5,注入 rowGap)、className;每行为 GlassCard + 头像圆 + 两行文字条 + 两个按钮条的脉冲占位,aria-hidden |
职责:模态框全家桶——普通 GlassModal 与确认用 GlassAlertDialog,均为 Portal + 复合(compound)API。
导出:GlassModal、GlassModalCompound、GlassAlertDialog、GlassAlertDialogCompound、类型 GlassModalProps、GlassAlertDialogProps
主要依赖:react(createContext/forwardRef/useContext/useEffect)、react-dom(createPortal)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
ModalProps |
modal.tsx:21 | isOpen、onOpenChange(open)、children |
ModalBackdropProps |
modal.tsx:27 | isDismissable(默认 true)、variant(声明但未被消费)、className |
ModalContainerProps |
modal.tsx:34 | size(xs/sm/md/lg/xl/2xl/full)、placement(center/top/bottom)、scroll(inside/outside,当前未参与渲染) |
CONTAINER_SIZES |
modal.tsx:44 | size→max-width:xs 360 / sm 400 / md 560 / lg 720 / xl 1100 / 2xl 1000 / full calc(100vw-2rem) |
GlassModal |
modal.tsx:54 | 打开时挂 Esc 关闭 + 锁定 body 滚动(:55-67),Portal 到 body 并提供 close Context;关闭时不渲染任何节点 |
Backdrop |
modal.tsx:83 | 全屏遮罩 lg-scrim z-[100](黑 35% + 轻模糊),入场用 lg-backdrop-in(渐显背景色,不是 opacity 淡入)——它是 Dialog 的父元素,用淡入会让内部玻璃在入场期间发虚;isDismissable 时 mousedown 点击自身关闭 |
Container |
modal.tsx:94 | 定位与宽度容器,placement 决定上下对齐 |
Dialog |
modal.tsx:123 | role="dialog" aria-modal="true",lg-float-in lg-regular rounded-2xl(入场:translateY(24px)+scale(0.96) → 1,阴影同步扩散,slow 档) |
Header / Heading / Body / Footer |
modal.tsx:130 / 134 / 138 / 149 | 结构分区;Heading 为 h2 text-lg;Body 是 forwardRef(displayName GlassModalBody)默认 overflow-y-auto 并支持 onScroll |
CloseTrigger |
modal.tsx:153 | 右上角 x 按钮,读 Context 关闭 |
GlassModalWithSub(即 GlassModalCompound) |
modal.tsx:174 / 194 | 挂载 8 个子件:Backdrop/Container/Dialog/Header/Heading/Body/Footer/CloseTrigger |
AlertDialogProps / GlassAlertDialog |
modal.tsx:201 / 209 | 与 GlassModal 同构(Esc + 锁滚动 + Portal) |
AlertDialogBackdrop / AlertDialogContainer / AlertDialogDialog |
modal.tsx:245 / 259 / 263 | 遮罩(不可点击关闭,入场同样用 lg-backdrop-in)、限宽 400px 容器、role="alertdialog" 面板 |
ICON_TONES / AlertDialogIcon |
modal.tsx:266 / 274 | 圆形图标底色(success/warning/danger/info/default),status 默认 info,内嵌感叹号 SVG |
AlertDialogHeader / Heading / Body / Footer / CloseTrigger |
modal.tsx:285 / 289 / 293 / 297 / 301 | 结构件;CloseTrigger 为绝对定位右上 x |
GlassAlertDialogWithSub(即 GlassAlertDialogCompound) |
modal.tsx:322 / 344 | 挂载 9 个子件(比 Modal 多一个 Icon) |
内部结构(modal.tsx 共 345 行):
| 行号区间 | 内容 |
|---|---|
| 5-19 | GlassModal 用法注释(compound API 示例) |
| 21-52 | 三个接口定义 + size 到宽度映射表 |
| 54-195 | GlassModal 及其 8 个子件实现、复合类型断言、导出 |
| 197-207 | GlassAlertDialog 分隔注释 + 接口与 Context |
| 209-320 | GlassAlertDialog 及其 9 个子件实现 |
| 322-345 | 复合类型断言、子件挂载与导出 |
备注:Container 接收 scroll prop 但实现未使用;Backdrop 声明了 variant 却不消费(confirm-dialog.tsx:61 传 variant="blur" 无效);xl(1100px) 比 2xl(1000px) 更宽,尺寸序列不单调。
职责:数字步进器(减号 / 数字输入 / 加号),通过 Context 共享取值与上下限。
导出:NumberField、GlassNumberField(别名,均带 .Group/.DecrementButton/.IncrementButton/.Input)
主要依赖:react(createContext/useContext)、@/lib/utils、./input(GlassInput)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassNumberFieldProps |
number-field.tsx:5 | value(默认 0)、onChange(value)、minValue、maxValue、aria-label、className、children |
NumberFieldCtx |
number-field.tsx:15 | 传递 { value, min, max, onChange } |
NumberField |
number-field.tsx:27 | 根容器 role="group",onChange 缺省为空函数 |
NumberField.Group |
number-field.tsx:56 | 横排分组容器(与根重复一层相同样式) |
NumberField.DecrementButton |
number-field.tsx:70 | 减一;value <= min 时禁用(:78);9x9 方钮内嵌减号 SVG |
NumberField.IncrementButton |
number-field.tsx:106 | 加一;value >= max 时禁用(:114) |
NumberField.Input |
number-field.tsx:142 | 基于 GlassInput type="number" 居中显示;NaN 回填 0(:153) |
备注:步长固定 1,无 step prop;输入非法值被归一为 0 而非保留空串。
职责:页面级页头(大标题 + 描述 + 右侧操作区),支持锚点 id 供站内搜索跳转。
导出:PageHeader
主要依赖:clsx
| 符号 | 位置 | 说明 |
|---|---|---|
PageHeaderProps |
page-header.tsx:3 | title(必填)、description、actions(右上按钮组)、id(锚点)、className |
PageHeader |
page-header.tsx:16 | <header id>;标题 text-2xl lg:text-3xl font-bold,移动端纵向、桌面横向两端对齐(:27) |
职责:进度展示两件套——环形 ProgressCircle(含不定量加载态)与线性 Meter,均为 Context 驱动的复合组件。
导出:ProgressCircle、Meter、GlassProgressCircle(别名)、GlassMeter(别名)
主要依赖:react(createContext/useContext)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
ProgressCtx |
progress.tsx:21 | 传递 { size, strokeWidth, value, isIndeterminate } |
ProgressCircle |
progress.tsx:28 | 根组件。props:isIndeterminate(默认 false,动画旋转)、size(sm 24 / md 32 / lg 40 或数字,默认 md)、value(0-100 被 clamp)、aria-label、className;线宽自动 max(2, round(px/10))(:44);根节点带 text-primary,弧线继承其 currentColor(调用方传 text-* 即可换色);输出 role="progressbar",svg 整体 -rotate-90 |
ProgressCircle.Track |
progress.tsx:74 | <g> 分组包裹 |
ProgressCircle.TrackCircle |
progress.tsx:84 | 底环(text-separator),半径 (size-strokeWidth)/2 |
ProgressCircle.FillCircle |
progress.tsx:100 | 进度弧(text-current,继承根节点色),strokeDashoffset 按 value 计算;不定量时固定 75% 弧长 + animate-spin(:104, :120) |
GlassProgressCircle |
progress.tsx:138 | ProgressCircle 别名(loading-block.tsx 使用) |
MeterCtx / Meter |
progress.tsx:142 / 144 | 线性进度根:props value(clamp 0-100)、aria-label、className;role="meter" |
Meter.Output |
progress.tsx:170 | 百分比文本,默认 sr-only |
Meter.Track |
progress.tsx:177 | h-2 圆角轨道 |
Meter.Fill |
progress.tsx:191 | 主色填充,宽度 value%,500ms 过渡 |
GlassMeter |
progress.tsx:205 | Meter 别名 |
备注:圆环与进度条共用 value 语义(0-100),isIndeterminate 时 aria-valuenow 置空。环的描边一律走原色:根节点默认 text-primary,调用点统一传 text-primary;按 globals.css 的状态色约定,--primary-text 这类「文字版」颜色只用于文字,描边/边框/背景保持原色。FillCircle 改用 text-current 后,调用方在根节点上传的 text-* 才会真正生效(此前弧线写死 text-primary-text,调用点的换色类全部无效)。
职责:设置/分区卡片——标题 + 描述 + 操作区 + 内容区的标准化外壳。
导出:SectionCard
主要依赖:@/components/ui/glass(GlassCard)、clsx
| 符号 | 位置 | 说明 |
|---|---|---|
SectionCardProps |
section-card.tsx:4 | id(锚点)、title、description、actions(右侧按钮)、padded(默认 true 是否内边距)、flush(移除内边距用于自定义布局)、className、children(必填) |
SectionCard |
section-card.tsx:17 | 基于 GlassCard,hover 时 shadow-md + scale 1.01(:32);有 title/actions 才渲染头行(:36);内容区 padding 规则见 :57-63 |
备注:padded 与 flush 同时为 true 时以 flush 优先(内容 p-0,头行仍保留 padding)。
职责:自定义下拉选择器(Portal 定位菜单),提供 GlassSelect 与薄封装 Select。
导出:Select、GlassSelect、类型 GlassSelectOption、GlassSelectProps
主要依赖:react(useEffect/useRef/useState)、react-dom(createPortal)、@/lib/utils、@/components/ui/app-icon(ChevronDownIcon)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassSelectOption |
select.tsx:6 | { value, label }。label 类型为 ReactNode(原为 string;字符串本身即合法 ReactNode,因此是向后兼容的类型放宽)。放标识时传 <AccountNameLine game={...}>{nickname}</AccountNameLine> |
GlassSelectProps |
select.tsx:18 | value(可 null)、options、onChange(value)、className、placeholder、maxMenuHeight(默认 288)、highlightSelected(默认 false,选中项加粗) |
GlassSelect |
select.tsx:28 | 触发按钮 + fixed 定位菜单(z-[9999],按下拉框底部 top = rect.bottom + 4);菜单宽度 max(150, 触发器宽度)。触发器文案用 min-w-0 flex-1 text-left(由 label 内部自行 truncate) |
SelectProps / Select |
select.tsx:179 / 190 | 薄封装:回调取 onValueChange ?? onChange;两者都不传时直接返回 null(不渲染) |
备注:打开状态下任意 document mousedown 或 window scroll(capture)都会关闭菜单(select.tsx:48-62)。菜单入场用 animate-scale-in(slow 档,-8px + scale(0.9),origin 由该类内置为 top center),关闭先置 closing 播 animate-fade-out、280ms 后卸载(closeMenu,:37-45),收尾不再被切掉。
职责:设置项行(左侧标题描述、右侧控件),及统一分隔线。
导出:SettingsRow、SettingsDivider
主要依赖:clsx
| 符号 | 位置 | 说明 |
|---|---|---|
SettingsRowProps |
settings-row.tsx:3 | id(锚点)、title(必填)、description、control(必填,右侧控件)、className |
SettingsRow |
settings-row.tsx:16 | 移动端纵向、桌面两端对齐;左区 flex-1,右区 shrink-0 |
SettingsDivider |
settings-row.tsx:43 | h-px bg-separator 全宽线,role="separator" |
职责:骨架屏占位块(脉冲动画)。
导出:Skeleton、GlassSkeleton
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassSkeletonProps |
skeleton.tsx:3 | 继承 HTMLAttributes<HTMLDivElement>(自定宽高通过 className/style) |
Skeleton |
skeleton.tsx:5 | aria-hidden + animate-pulse rounded-lg;仅当 className 不含 bg- 时才补默认 bg-muted(:6, :12) |
GlassSkeleton |
skeleton.tsx:20 | 别名 |
职责:基于 Radix 的滑块(轨道 + 范围 + 手柄),含内阴影玻璃质感样式。
导出:Slider
主要依赖:@radix-ui/react-slider(^1.4.7)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
Slider |
slider.tsx:5 | forwardRef 直通 SliderPrimitive.Root 全部 props(value/min/max/step/onValueChange 等 Radix 原生 API);Thumb 自动继承根的 aria-label(:21);displayName 取自 Radix Root |
备注:无自定义 props,样式常量写死在 Track/Range/Thumb 上;value 是 Radix 的数组形式。
职责:SVG 环形加载指示器。
导出:Spinner、GlassSpinner
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassSpinnerProps |
spinner.tsx:3 | size(sm 16 / md 24 / lg 32 或数字,默认 sm)、color(current/default/primary,默认 current)、className |
colorMap |
spinner.tsx:9 | color → text 类映射;primary 取原色 text-primary(描边不套用「文字版」变体) |
RADIUS / CIRCUMFERENCE |
spinner.tsx:18 / :19 | viewBox 24 下的环参数(r=10 与周长),底圈与不定量弧共用 |
Spinner |
spinner.tsx:21 | animate-spin + 底圈 25% 透明;不定量弧同样是 stroke(r=10, strokeWidth=4) 并带 strokeLinecap="round"、strokeDashoffset 取 3/4 周长(:49-60),与 GlassProgressCircle 的不定量态同族;aria-hidden |
GlassSpinner |
spinner.tsx:65 | 别名(GlassButton 的 isLoading 使用) |
职责:账户同步状态徽标——把状态枚举映射为语义色 Chip。
导出:StatusBadge、SYNC_STATUS_META、类型 StatusTone、StatusConfig
主要依赖:@/components/ui/glass(GlassChip)、clsx、@/types(AccountSyncStatus)
| 符号 | 位置 | 说明 |
|---|---|---|
StatusTone |
status-badge.tsx:5 | success/warning/danger/default |
StatusConfig |
status-badge.tsx:7 | { tone, label, showLed? } |
SYNC_STATUS_META |
status-badge.tsx:13 | AccountSyncStatus → 配置:HYTOKEN_EXPIRED→danger/"EXPIRED"、FAILED→warning/"SYNC FAILED"、SYNCING→default/"SYNCING" |
StatusBadge |
status-badge.tsx:27 | props:config(StatusConfig)、className;渲染 GlassChip size="sm" variant="soft",强制 font-bold tracking-wider text-[10px] h-5 |
备注:StatusConfig.showLed 字段本文件未消费(仅作元数据,供其他组件决定是否画状态灯)。
职责:独立的状态圆点(账号卡片用),带可选 ping 动画与 glow 阴影。
导出:StatusDot、类型 StatusDotTone
主要依赖:clsx
| 符号 | 位置 | 说明 |
|---|---|---|
StatusDotTone |
status-dot.tsx:3 | success/danger/warning/default |
StatusDotProps |
status-dot.tsx:5 | tone(必填)、ping(默认 false)、className |
TONE_STYLES |
status-dot.tsx:11 | tone → { dot, ping, shadow } 三组类名;default 无 ping/shadow |
StatusDot |
status-dot.tsx:41 | 固定 12x12 圆点 + 可选 animate-ping opacity-20 覆盖层 |
备注:与 app-icon.tsx:41 的 StatusDot 是两个不同实现(此处 props 为 ping,彼处为 pulse + size)。
职责:开关组件三件套——复合式 GlassSwitch(Root+Control+Thumb 分离)与一体式 Switch/SwitchThumb,均基于 Radix。
导出:Switch、SwitchThumb、GlassSwitch
主要依赖:@radix-ui/react-switch(^1.3.7)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassSwitchProps |
switch.tsx:5 | Radix Root props(省略 onCheckedChange)+ isSelected、onValueChange(selected)、isDisabled |
GlassSwitch |
switch.tsx:14 | disabled 或 isDisabled 时禁用;未传 children 时默认渲染裸 SwitchPrimitive.Thumb(无轨道样式)(:37),正常用法是传入 GlassSwitch.Control 包 GlassSwitch.Thumb |
GlassSwitch.Control |
switch.tsx:42 | 轨道:h-6 w-11 圆角,group-data-[state=checked] 时变主色 |
GlassSwitch.Thumb |
switch.tsx:63 | 手柄:白色 20x20,checked 时 translate-x-[22px] |
SwitchThumb |
switch.tsx:77 | forwardRef 的 Radix Thumb,样式同上但用 data-[state=checked](无 group 前缀) |
Switch |
switch.tsx:93 | forwardRef 一体式 Root:轨道样式直接写在 Root 上,children 缺省渲染 SwitchThumb(:105) |
备注:两套 API 选一即可——GlassSwitch(分体,需显式组合 Control/Thumb)或 Switch(一体,零子件可用);GlassSwitch 不传 children 时回落到本项目的 Thumb(此前是 Radix 原生 Thumb,无样式也无过渡,即「库里的组件没动画」)。轨道底色走 --lg-dur-base(260ms) + --lg-ease-standard,滑块位移走 --lg-dur-press(340ms) + --lg-ease-spring(带惯性过冲,滑到位而非瞬移)。选中态不闪烁的另一个前提在 globals.css:.lg-control::before 的填充必须用 background-color 过渡——原先的 background: none 简写不可过渡,会在铺上 bg-primary 的同一帧整块消失。
页面里自制的分段控件(同一套动效约定):区域信息切换(domain.tsx)、抽卡记录的分类选择与稀有度筛选(gacha-records.tsx)、仪表盘背景模式(layouts/dashboard.tsx)、标题栏「数据/游戏」(.titlebar-mode-toggle)都不是 Radix Switch,而是等宽分格 + 一个绝对定位的滑动指示器:指示器宽度 calc((100% - 两侧内边距) / 项数)、transform: translateX(选中下标 * 100%)、过渡 transform var(--lg-dur-slow) var(--lg-ease-inertia)。选中态由指示器承担,按钮本身只负责文字色——不要再让每个按钮各自换底(那是瞬间换底,不是惯性滑动),也不要在撑满宽度的按钮上加 hover:scale-*(必然溢出父容器圆角)。
- 几何必须给出过冲余量。宿主可以留
overflow-hidden(挡拨杆那个0 1px 4px外阴影溢出胶囊),但拨杆不能正好占满半格——标题栏原来用calc(50% - 2px),选中位右边缘刚好贴到 padding box 边界,inertia 的回弹整块被裁掉,看上去只剩匀速平移。现在改为容器p-[3px]+ 拨杆calc(50% - 3px),选中位右侧余 3px,容得下约 2.5px 的回弹。 - 惯性曲线要够猛:用
--lg-ease-inertia(1.4),不要用--lg-ease-spring(1.2)——后者只有约 4% 过冲,49px 的拨杆才弹 2px,肉眼等于没有。
标题栏那个是特例(切模式时标题栏会被重建):DashboardLayout 的两个模式分支各渲染一份 <CustomTitlebar />,且它处于不同的子节点索引(游戏分支是第 3 个,数据分支前面多一个 dashboard-veil 是第 4 个),React 按位置协调时类型对不上,于是切模式会卸载重建整棵标题栏——新挂载的拨杆一开始就带 is-game,transition 永远没机会播放。所以拨杆用「挂载即播放」的 animation(titlebar-knob-in-left/right)兜住,fill-mode 取 backwards 而非 both:动画结束不占着 transform,将来若把标题栏改成常驻挂载,状态过渡照样能走 transition。要彻底修这个重建问题,需要把 <CustomTitlebar /> 提到两个分支之外(共同父级),属于主布局重构,尚未做。
职责:语义化表格复合组件(容器 + table + 表头/行/单元格)。
导出:Table、GlassTable(别名,带 7 个子件)
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassTableProps |
table.tsx:3 | 通用 { className?, children? } |
Table |
table.tsx:8 | 外层滚动容器(w-full overflow-x-auto) |
Table.ScrollContainer |
table.tsx:12 | 额外横向滚动容器 |
Table.Content |
table.tsx:16 | <table> 本体,支持 aria-label,border-collapse text-sm |
Table.Header |
table.tsx:32 | <thead> 并自动包裹一层 <tr>(:36),子件应直接写 <Column> |
Table.Column |
table.tsx:40 | <th>;isRowHeader=true 时 scope="row" 否则 scope="col" |
Table.Body |
table.tsx:51 | <tbody>,divide-y divide-separator/60 |
Table.Row |
table.tsx:55 | <tr>,hover 背景 + 微缩放 |
Table.Cell |
table.tsx:59 | <td>,px-4 py-2.5 |
备注:Table.Header 自带 <tr>,不要在其内部再写 <tr>。
职责:分段切换组(单选/多选胶囊组),基于 Radix ToggleGroup。
导出:ToggleGroup、ToggleGroupItem
主要依赖:@radix-ui/react-toggle-group(^1.1.19)、@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
ToggleGroup |
toggle-group.tsx:21 | forwardRef 直通 ToggleGroupPrimitive.Root(type="single"/"multiple"、value、onValueChange 等 Radix 原生 props);只做布局、不铺底色——轨道材质由调用点给出(标题栏用 .titlebar-mode-toggle,其余用 .lg-control / .lg-inset),避免分段控件比旁边浮层更实 |
ToggleGroupItem |
toggle-group.tsx:34 | forwardRef 直通 Item;选中态不再铺 bg-primary/20 实色(选中指示由外部拨杆/胶囊承担),hover 走 .lg-hoverable;圆角只由调用点给出(基类不写 rounded-*)——基类曾带 first:rounded-l-md / last:rounded-r-md(10px),与调用点的 rounded-full(calc(infinity * 1px))混用会触发 border-radius 重叠缩放,把具体值那一侧压成 0px,首/末项悬浮高亮块被渲染成直角并顶出胶囊 |
职责:纯 CSS 悬浮提示(group-hover 触发,绝对定位在触发元素上方),两套同构实现。
导出:Tooltip、GlassTooltip
主要依赖:@/lib/utils(cn)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassTooltipProps |
tooltip.tsx:3 | 继承 HTMLAttributes<HTMLSpanElement>;声明了 delay 但实现未使用 |
GlassTooltip |
tooltip.tsx:7 | 触发容器 group relative inline-flex |
GlassTooltip.Content |
tooltip.tsx:15 | role="tooltip",默认透明,group-hover 时显现并上浮 4px(:29) |
TooltipProps / Tooltip |
tooltip.tsx:40 / 42 | 同 GlassTooltip,容器类名改为 peer/group |
Tooltip.Content |
tooltip.tsx:50 | 除 group-hover 外还响应 peer-focus-within(键盘聚焦也显示,:65) |
备注:提示内容必须作为触发元素的兄弟节点(依赖 group/peer 选择器);delay prop 无效(无延迟实现)。
职责:原生单选组(radio group),不依赖 Radix ToggleGroup。用于「N 选一」且需要明确必选语义的场景(如设置页的网络代理协议 HTTP / SOCKS5)。
导出:GlassRadioGroup(Object.assign 复合组件,挂在 GlassRadioGroup.Item 上)、类型 GlassRadioGroupProps、GlassRadioGroupItemProps
主要依赖:react(Context/useRef/useMemo)、@/lib/utils(cn)。不依赖 @radix-ui/react-toggle-group——Radix 的 type 只接受 single/multiple 与旧实现冲突。
| 符号 | 位置 | 说明 |
|---|---|---|
GlassRadioGroupProps |
radio-group.tsx:4-19 | value(必填当前值)、onValueChange?、children?、className?、isDisabled?、aria-label?、orientation?(horizontal(默认)/vertical,仅影响方向键) |
GlassRadioGroupItemProps |
radio-group.tsx:21-26 | value(必填)、children?、className?、isDisabled? |
RadioGroupContext |
radio-group.tsx:28-38 | 内部 Context:value/onValueChange/isDisabled/orientation/register/order |
GlassRadioGroupRoot(私有) |
radio-group.tsx:47-147 | 根容器:role="radiogroup" + aria-orientation;←/→(纵向为 ↑/↓)轮转并聚焦下一项;注册表用 Map 保存各 item 的 DOM 节点 |
GlassRadioGroupItem(私有) |
radio-group.tsx:149-197 | 每项为原生 <button role="radio" aria-checked>,选中项 tabIndex=0、其余 -1(roving tabindex),重复点击不重复触发 onValueChange;左侧小圆点表达选中态 |
GlassRadioGroup(export) |
radio-group.tsx:199-201 | Object.assign(GlassRadioGroupRoot, { Item: GlassRadioGroupItem }) |
备注:GlassRadioGroup.Item 必须在 GlassRadioGroup 内部使用(否则抛错「必须放在 GlassRadioGroup 内部」);默认横向、整组宽度 w-fit,容器胶囊边框与 ToggleGroup 保持一致但内边距/字号更大(h-8、text-sm)以便承载协议名。
职责:玻璃拟态风格统一出口(barrel):把各基础文件里的 Glass* 组件与类型再导出,供 @/components/ui/glass 一键引入。
导出:见下表(32 行导出语句)
主要依赖:同目录上级各模块(../button、../radio-group、../modal 等 20 个来源文件)
| 符号 | 位置 | 说明 |
|---|---|---|
GlassButton + 4 个类型 |
glass/index.ts:1 | 来自 ../button |
GlassCard + GlassCardProps |
glass/index.ts:2 | 来自 ../card |
GlassInput + GlassInputProps |
glass/index.ts:3 | 来自 ../input |
GlassSwitch + GlassSwitchProps |
glass/index.ts:4 | 来自 ../switch |
Slider |
glass/index.ts:5 | 来自 ../slider(无 Glass 前缀) |
GlassCheckbox + 类型 |
glass/index.ts:6 | 来自 ../checkbox |
GlassChip + GlassChipProps、GlassChipTone |
glass/index.ts:7 | 来自 ../badge |
GlassSkeleton + 类型 |
glass/index.ts:8 | 来自 ../skeleton |
GlassKbd + 类型 |
glass/index.ts:9 | 来自 ../kbd |
GlassAlert + GlassAlertProps、GlassAlertStatus |
glass/index.ts:10 | 来自 ../alert |
GlassTooltip + 类型 |
glass/index.ts:11 | 来自 ../tooltip |
GlassLabel + 类型 |
glass/index.ts:12 | 来自 ../label |
GlassLink + 类型 |
glass/index.ts:13 | 来自 ../link |
GlassSpinner + 类型 |
glass/index.ts:14 | 来自 ../spinner |
GlassProgressCircle、GlassMeter |
glass/index.ts:15 | 来自 ../progress |
GlassTable |
glass/index.ts:16 | 来自 ../table |
GlassNumberField + 类型 |
glass/index.ts:17 | 来自 ../number-field |
GlassSelect + GlassSelectProps、GlassSelectOption |
glass/index.ts:18 | 来自 ../select |
GlassRadioGroup + GlassRadioGroupProps、GlassRadioGroupItemProps |
glass/index.ts:19-23 | 来自 ../radio-group(单选组,GlassRadioGroup.Item 复合用法) |
GlassInputOTP + 类型 |
glass/index.ts:24 | 来自 ../input-otp |
GlassModal、GlassModalCompound、GlassAlertDialog、GlassAlertDialogCompound + 2 类型 |
glass/index.ts:25-32 | 来自 ../modal |
备注:此桶不含无 Glass 前缀的组件(Button、Input、Checkbox、Select、Table、Kbd 等),也不含 EmptyState、StatusBadge、LoadingBlock、ConfirmDialogHost 等业务化组件——这些需直接从各自文件引入。
职责:tailwind-variants 定义的排版原语(渐变大标题、副标题)。
导出:title、subtitle
主要依赖:tailwind-variants(^4.0.1,tv)
| 符号 | 位置 | 说明 |
|---|---|---|
title |
primitives.ts:3 | 渐变文字标题。variants:color(violet/yellow/blue/cyan/green/pink/foreground 七种渐变)、size(sm/md/lg,默认 md)、fullWidth;compoundVariants 为所有 color 附加 bg-clip-text text-transparent bg-gradient-to-b(:27-40)。用法 title({ color: "violet", size: "lg" }) |
subtitle |
primitives.ts:43 | 副标题 text-lg lg:text-xl text-muted;variant fullWidth(默认 true,!w-full) |
备注:渐变色值是硬编码十六进制(如 #FF1CF7),与主题 token 不联动。
职责:业务图标集合——品牌图标(内联 SVG)、lucide 图标的 MorphIcon 封装、以及 key 到图标的映射表 CardIcon。
导出:Logo、DiscordIcon、TwitterIcon、GithubIcon、29 个命名图标、3 个别名、CardIcon
主要依赖:lucide(^1.70.0,35 个图标)、morphicons/react(MorphIcon)、@/components/morph-icon(createMorphIcon、IconSvgProps)、morphicons(IconInput 类型)
| 符号 | 位置 | 说明 |
|---|---|---|
Logo |
icons.tsx:43 | 品牌 Logo SVG(32x32 viewBox),props:size(默认 36)/height + 全部 SVGProps |
DiscordIcon |
icons.tsx:64 | 内联路径的 Discord 图标(createMorphIcon 接收路径数组) |
TwitterIcon |
icons.tsx:68 | Twitter/X 图标 |
GithubIcon |
icons.tsx:72 | GitHub 图标 |
MoonIcon/SunIcon/ComputerIcon/HeartIcon/SearchIcon/HomeIcon/ChartIcon/SettingsIcon/UsersIcon/ProjectsIcon/CalendarIcon/HelpIcon/DeveloperIcon/AccountIcon/StarIcon/GachaIcon/MedalIcon/BookmarkIcon/TagIcon/FolderIcon/BellIcon/PlusIcon/ChevronLeftIcon/EditIcon/TrashIcon |
icons.tsx:76-100 | lucide 图标(Moon/Sun/Monitor/Heart/Search/Home/BarChart3/Settings/Users/FolderOpen/Calendar/HelpCircle/Code/User/Star/Sparkles/Award/Bookmark/Tag/Folder/Bell/Plus/ChevronLeft/Pencil/Trash2)的 MorphIcon 封装 |
TerminalIcon/CopyIcon/ExternalLinkIcon/GamepadIcon |
icons.tsx:101-104 | lucide 图标(Terminal/Copy/ExternalLink/Gamepad2)的 MorphIcon 封装,用于游戏启动快捷弹窗的命令/快捷方式/Steam 板块 |
MoonFilledIcon/SunFilledIcon/HeartFilledIcon |
icons.tsx:106 | 上述三个图标的再导出别名 |
CARD_ICON_MAP |
icons.tsx:108 | 23 个 key(home/chart/users/star/heart/bookmark/tag/folder/calendar/bell/settings/account/search/developer/projects/file/map/satellite/award/user/help/factory/sprout)→ lucide 图标数据 |
CardIcon |
icons.tsx:134 | props:iconKey(必填)、size(默认 24)、className + SVGProps;key 未命中映射时回退为纯文本 span 显示 key 本身(:141);渲染前剥离 from/to/ref(:142) |
备注:PlusIcon/ChevronLeftIcon/EditIcon/BellIcon 等名称与 ui/app-icon.tsx 中的同名导出重复,来自不同模块,import 时务必确认路径。
职责:morphicons 的工厂函数——把 lucide/路径数据包成支持 size 属性、可转发 ref 的统一图标组件。
导出:createMorphIcon、类型 IconSvgProps、MorphHandle、IconInput
主要依赖:morphicons/react(MorphIcon、MorphHandle)、morphicons(IconInput)、react
| 符号 | 位置 | 说明 |
|---|---|---|
MorphHandle / IconInput |
morph-icon.tsx:5 | 从 morphicons 包转出的类型(ref 句柄 / 图标输入数据) |
IconSvgProps |
morph-icon.tsx:7 | React.SVGProps<SVGSVGElement> + size?: number |
createMorphIcon |
morph-icon.tsx:11 | 入参 lucide 图标或路径数组;返回 forwardRef<MorphHandle, IconSvgProps> 组件,透传 size/className/style 及其余 SVG 属性,剥离 from/to/ref(:14);displayName 固定为 "MorphIcon" |
备注:这是 app-icon.tsx 与 icons.tsx 全部图标的公共底座;图标切换动效由 morphicons 库内部提供。
StatusDot重名双实现:app-icon.tsx:41(pulse+size)与status-dot.tsx:41(ping,固定 12px)。- 图标导出重名:
icons.tsx与app-icon.tsx均导出PlusIcon、ChevronLeftIcon、EditIcon、BellIcon。 - 死/无效 props:
input.tsx的variant、isClearable;tooltip.tsx的delay;modal.tsx的Backdrop.variant与Container.scroll;badge.tsx的 cvavariant/color变体值为空串(实际配色由 tone 表决定)。 - 注释与实现不符:
back-to-top.tsx:8-9注释称超过 50% 显示,实际p > 20(:32)。 - 尺寸序列异常:
modal.tsx:44-52中xl1100px 大于2xl1000px。 - 层级风险:
GlobalAlertHost为z-50,modal/AlertDialog 遮罩为z-[100],Select 菜单z-[9999],BackToTop 为z-[10003]。 - 依赖包名:图标库为
lucide(v1.70.0)而非常见的lucide-react;动效图标为morphicons(v2.0.2,2.0 起 filled 变体真实渲染为填充)。 GlassSelect关闭策略:菜单打开时任意滚动(capture 阶段)都会关闭菜单(select.tsx:42)。Select静默不渲染:未提供onValueChange/onChange时返回null(select.tsx:148)。confirmDialog依赖 Host:未挂载ConfirmDialogHost时永远 resolvefalse(confirm-dialog.tsx:18)。