定位现有实现请查 README.md 快速定位表,或 reference/frontend/(18 篇逐文件参考,含行号与 props 清单)。
| 目录 | 内容 | 对应 reference 文档 |
|---|---|---|
src/pages/ |
10 个页面(dashboard、account、char-select、characters、gacha-records、attendance、medals、todo、settings、developer) | pages_*.md |
src/layouts/ |
dashboard / default 布局 | pages_core.md |
src/components/ |
弹窗、面板、侧栏、图表等大组件(约 29 个 tsx,含 network-settings.tsx) |
components_dialogs.md、components_settings_panels.md、components_sidebar_charts.md、components_game_action_panel.md |
src/components/ui/ |
37 个通用 UI 组件 + glass/ |
ui_components.md |
src/components/cards/ |
卡片系统:registry/、base/、card-container.tsx、_template/ 与 11 张卡片(含 system-resource、todo) |
cards_core.md 及各 cards_*.md |
src/utils/ |
19 个 IPC 封装与工具(account/config/proxy/cardConfig/dashboard/role/launcher/update/image/logger…) | utils_services_a.md、utils_services_b.md |
src/types/ |
TypeScript 类型 | types_locales_styles.md |
src/locales/{zh,en}/ |
国际化资源 | 同上 |
src/styles/globals.css |
全局样式、主题变量、玻璃拟态类 | 同上 |
src/stores/、src/hooks/、src/lib/、src/config/ |
轻量状态、hooks、工具函数 | entry_and_app.md |
- 状态管理: 优先使用 React Hooks (
useState,useEffect);跨窗口/轻量全局状态用src/stores/(inAppBrowser.ts、launcherMode.ts)。 - 样式编写: 使用 Tailwind CSS 原子类 +
src/components/ui/glass的玻璃组件;深色模式通过globals.css的.dark类切换。 - 国际化: 用户可见文本必须通过
useTranslation从locales/获取;卡片文案放各自的locales/{zh,en}.json(合并进card命名空间)。 - 日志记录: 使用
src/utils/logger.ts的logger/logInfo/logError/logDebug,禁止console.log;日志级别由开发者模式下的log_level控制(关闭开发者模式时 DEBUG 自动屏蔽)。 - IPC 调用: 页面/组件不直接散写
invoke,统一走src/utils/*Service.ts封装;需要新增命令时先在封装层加函数(含类型与错误兜底)。 - 错误处理: 封装层统一
try/catch+logError,并明确失败时的返回值约定(空数组/false/null)。
卡片系统是约定式自动发现的,无需改核心代码(详见 card_development.md):
- 新建
src/components/cards/<card-name>/ - 添加
<card-name>.meta.json(id、name{zh,en}、description{zh,en}、icon、defaultSize、version、可选allowMultiple) - 添加
index.tsx默认导出组件,props 用BaseCardProps(roleId/cardId/settings/isEditMode) - 添加
locales/{zh,en}.json,组件内用t("card:key") - 若有配置项,在
src/types/card-settings.ts增加 settings 类型 - 重启开发服务器,卡片自动进入「添加卡片」对话框(
getAvailableCards())
渲染由 src/pages/dashboard.tsx:422 的 CardContainer 统一负责(拖拽、编辑模式、右键菜单),
布局持久化走 src/utils/dashboardConfig.ts(配置键 dashboard_tabs),配置读写走 src/utils/cardConfigService.ts。
- 在
src/pages/创建页面组件 - 在
src/App.tsx用React.lazy(() => import("@/pages/..."))声明页面组件,再用react-router-dom的<Route>注册(页面加载态由layouts/dashboard.tsx的<main>内Suspense兜底,勿在路由内重复包裹) - 如路由需可恢复,把路径加入
App.tsx的ROUTES白名单 - 导航入口:
src/components/dashboard-sidebar.tsx或navbar.tsx(入口与侧栏结构见 components_sidebar_charts.md)
打包:页面按路由懒加载;第三方库由
vite.config.ts的build.rolldownOptions.output.codeSplitting.groups拆成vendor-react/vendor-echarts/vendor-zrender/vendor-i18n/vendor-ui/vendor-dnd/vendor-tauri等 chunk,避免单个 chunk 超过 Vite 的 500 kB 告警阈值。引入echarts时务必用echarts/core+ 按需echarts.use(...),不要全量import * as echarts from "echarts"。
⚠️ 改codeSplitting.groups必须保持 chunk 之间无回边。判定包归属时用「包根锚定」(/[\\/]node_modules[\\/](?:\.pnpm[\\/][^\\/]+[\\/])?react-dom[\\/]/),不要用宽泛的子串匹配 —— 后者会命中@radix-ui/react-dom等包,把 Radix 塞进vendor-react,Rolldown 便会报告Circular chunk: vendor-react -> vendor-ui -> vendor-react并重排模块顶层声明,React 工厂变量在其模块求值前被写入,浏览器抛Cannot set properties of undefined (setting 'Activity'),前端在main.tsx之前就崩溃、Splash 永远停在「正在启动…」。该故障的产物哈希与平台相关,只在 Linux 发行版稳定复现,Windows 上生成同一份配置的产物却正常,所以务必以pnpm build输出中是否出现Circular chunk:为准,出现即视为构建失败。
- 在
src/utils/对应服务文件(或新建xxxService.ts)中封装invoke - 定义请求参数与返回值的 TypeScript 类型(放
src/types/或服务文件内) try/catch捕获异常并用logger记录,明确失败返回值- 同步在 reference/ipc_index.md 与
reference/frontend/utils_services_*.md补一行
- 放
src/components/ui/(不要放ui/glass/,该目录只放 barrelindex.ts),遵循现有组件的 props 风格(受控isOpen/onOpenChange、className合并用cn) - 如需从
@/components/ui/glass统一引入,在ui/glass/index.ts里追加一行再导出 - 复用
glass/的视觉变量,支持深色模式 - 玻璃模糊只能写在
::before层:globals.css里.glass-surface/.glass-surface-strong/.glass-control-surface/.glass-field/.account-glass-*的底色与backdrop-filter都在::before(position:absolute; inset:0; z-index:-1)上,元素自身保持background: transparent。原因是 Linux(WebKitGTK)在运行时改写--glass-blur(设置页拖动「毛玻璃清晰度」)后,会把背景模糊层重新合成到元素内容之上,整张卡片连文字一起糊掉;模糊放进没有子内容的伪元素就不会波及内容。新增玻璃类请沿用同一模式,并把类名补进globals.css末尾@layer components的定位列表(放低优先级层,才不会盖掉fixed/sticky/absolute/z-*工具类) - 玻璃宿主与其祖先不得带
filter,也不得带opacity < 1,入场动画不得保留终态:宿主/祖先一旦有filter(含brightness-*这类工具类),或它的opacity < 1(静态写死,或入场动画的淡入帧),或元素带着 fill-mode 为both/forwards的animation,它就成为 backdrop root,其::before的backdrop-filter再也采不到背后的壁纸/视频——玻璃立刻(或永久)退化成一块半透明色。这正是「悬停按钮变半透明」「右键菜单没有毛玻璃」的根因。落地做法:交互提亮用globals.css的.lg-glass-interactive(filter落在::before上);入场动画只做 transform(位移/缩放),不做淡入,fill-mode 一律backwards(只有退出动画才用both);需要淡入的只有「既不是玻璃宿主也不是玻璃祖先」的层(如独立遮罩,改用lg-backdrop-in渐变背景色)。完整对照与实测数据见 types_locales_styles.md 的「backdrop root 约束」 - 组件清单与 props 见 ui_components.md
- 单选/分段语义优先用
GlassRadioGroup(<GlassRadioGroup value=… onValueChange=…>+<GlassRadioGroup.Item value=…>,见ui/radio-group.tsx),不要用 Radix ToggleGroup 冒充单选组(其type只接受single/multiple,传horizontal/vertical会踩类型陷阱)
- 需求分析: 确定卡片展示的数据字段及来源(是否已有后端命令 → 查 ipc_index.md)
- 后端准备: 若无命令,按 backend_development.md 新增 command + service 方法
- 前端实现:
- 复制
src/components/cards/_template/并重命名,写meta.json+index.tsx+locales - 数据获取用
useCardData(src/components/cards/base/use-card-data.ts) - 需要用户配置时走
CardConfigService并在types/card-settings.ts补类型 - 「添加卡片」对话框(
src/components/add-card-modal.tsx)会自动收录,无需改代码
- 复制
- 测试验证: 运行项目,测试卡片添加、数据显示、拖拽排序、配置保存与卡片删除后的配置清理
改动涉及新增/重命名文件、导出符号或 IPC 命令时,同步更新对应的 reference/*.md(格式见 README.md)。