diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index fb7a7fda..a91fe313 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -35,7 +35,7 @@ jobs: run: npm run build # env: # VITE_BASE_PATH: './' - + - name: Verify and fix web build shell: bash run: | @@ -226,9 +226,8 @@ jobs: packageJson.scripts = packageJson.scripts || {}; // 确保构建脚本使用正确的基础路径 - packageJson.scripts.build = 'vite build --base=./'; - packageJson.scripts['build:electron'] = 'vite build --base=./ && electron-builder'; - packageJson.scripts.dist = 'electron-builder --publish=never'; + packageJson.scripts.build = 'vite build --base=./ && npm run check:bundle-size'; + packageJson.scripts['build:electron'] = 'vite build --base=./ && npm run check:bundle-size && electron-builder'; // Ensure proper base path for Electron if (!packageJson.build) packageJson.build = {}; @@ -237,7 +236,7 @@ jobs: }; packageJson.scripts.electron = 'electron .'; packageJson.scripts['electron-dev'] = 'NODE_ENV=development electron .'; - packageJson.scripts.dist = 'electron-builder'; + packageJson.scripts.dist = 'npm run check:bundle-size && electron-builder --publish=never'; packageJson.build = { appId: 'com.github-stars-manager.app', diff --git a/audit-summary.md b/audit-summary.md new file mode 100644 index 00000000..0cd2b591 --- /dev/null +++ b/audit-summary.md @@ -0,0 +1,188 @@ +# 审计与质量门禁摘要 + +## 代码与类型审计 + +本轮改造将前端呈现层统一到 **shadcn/ui 风格组件与 Radix Primitives**,并清理共享原语的 Fast Refresh 非组件导出、未使用 Hook 依赖、测试文件隐式依赖和 GraphQL 响应隐式类型。`src/services/githubListsApi.ts` 的改动仅限于 GraphQL wire 类型、nullable `nodes`/`description` 的安全归一化和内部请求签名清理;网络请求语义、store、同步流程、AI 分析、仓库动作和权限逻辑均保持不变。 + +CodeRabbit 首轮报告中的共享 Button 变体、粘连 Tailwind token、重复 dark-mode 类、nullable GraphQL nodes、`tailwindcss-animate` 注册、语义色 alpha placeholder、border token、Slider thumb accessible name、Tooltip Portal、Dialog 标题、Select accessible labeling、ConfirmDialog 双触发、RadioGroup accessible name、DropdownMenu 语义 role 以及冗余 outside-click 逻辑均已核对并修复。ReleaseTimeline、RepositoryEditModal、CategoryPanel、WebDAVPanel、DiscoveryView、SearchBar 和全局 legacy CSS 的剩余 findings 也已完成收尾。 + +## 设计系统与无障碍审计 + +全局主题使用 shadcn 默认语义变量:`background`、`foreground`、`card`、`popover`、`primary`、`secondary`、`muted`、`accent`、`destructive`、`border`、`input`、`ring` 和 `radius`。主操作使用 `primary/primary-foreground`,中性工具栏和 icon-only 控件使用 `ghost`,次级预设使用 `outline`;空状态刷新按钮、缓存状态和深色模式文本均已检查实际背景下的对比度。 + +Radix Dialog、AlertDialog、Select、DropdownMenu、Tooltip、Slider、RadioGroup、Checkbox、Switch、Tabs 和 Popover 均使用共享封装。ReadmeModal 与 ForkTimeline 的用户可见选择控件均由共享 Radix Select 渲染;测试已改为验证真实的 combobox/option 用户路径。RepositoryCard 的菜单测试已改用 `menuitem` 语义,并以 userEvent 验证 pointer、keyboard、card whitespace、outside pointer 和 Escape 行为。 + +## 安全审计 + +`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。仓库没有使用 `react-router-dom`,未使用依赖及其传递漏洞来源已移除,没有执行破坏性路由升级。 + +## UI 与交互走查 + +生产预览已走查仓库页、设置页、AI 配置页、新增 AI 表单、WebDAV 空状态、备份恢复面板、主题共享控件、导航 active 状态和 AI 搜索空查询 disabled 状态。Radix Select 打开与切换、Checkbox 展开默认提示词、Textarea、Switch checked 状态、取消表单、空状态恢复、仓库操作菜单和 README 语言切换均已回归验证。测试专用的 jsdom Pointer Capture 与 `scrollIntoView` no-op polyfill 只位于 `src/test/setup.ts`,不进入生产代码路径。 + +## 最终质量门禁 + +| 检查项 | 结果 | +|---|---| +| `npm run lint` | 通过,0 errors、0 warnings | +| `npx tsc -b` | 通过,无 TypeScript 诊断 | +| `npm run test:run` | 基线套件为 29 个文件/326 个测试;最终提交新增 SliderInput、StepperInput 与 RepositoryCard 回归后为 31 个文件/332 个测试,全部通过且无 React/Radix act、Unhandled 或 Warning 输出 | +| `npm run build` | 通过;legacy 入口 2,789.46 kB;独立 checker 实测 2,724.09 KiB,低于 3,000 KiB hard budget,无 chunk warning | +| `git diff --check` | 通过 | +| `npm audit --omit=dev --audit-level=high` | `found 0 vulnerabilities` | +| ReadmeModal + RepositoryCard 定向回归 | 通过,13/13;无测试 warning | +| SliderInput + StepperInput 交互回归 | 通过,3/3;pointer 精确命中 Radix Slider.Root 后验证 `1→6`,keyboard 验证 `1→2`,Stepper keyboard-click 亦通过,无 warning | + +## 交付与审查状态 + +首轮 CodeRabbit findings 已全部修复并通过上述质量门禁。第二次 `@coderabbitai full review` 报告的 2 个 outside-diff findings 已修复:SubscriptionRepoCard 的确认取消 Star 按钮已使用 destructive 语义色,ForkTimeline 的 page-size 与 target-branch SelectTrigger 已关联可见 label。最终 full review 又发现 SubscriptionRepoCard 卡片容器仍使用 `bg-white`,现已改为 `bg-card` 并清理重复 dark token。最后一轮 lint、tsc、332 个测试、build、diff check 与安全审计均通过。最终 full review 随后报告 11 个 actionable comments:CategoryEditModal/GistEditorModal 表单标注、GistView 清除按钮、Modal/DialogDescription 策略、ReadmeModal 关闭按钮、RepositoryEditModal 保留名校验、RepositoryList/SearchDemo ghost 变体、AIConfig/Backend/AlertDialog/StepperInput 无障碍或键盘行为,以及 ui-walkthrough Slider 证据和 vite bundle budget nitpick;此前 11 个 findings 已全部修复并完成针对性回归。Modal/SettingsPanel/ReadmeModal 使用明确的 Dialog description policy,表单和 Radix 控件均补齐 label/id,`none` sentinel 已受保护,StepperInput 已支持 keyboard click,Slider pointer/keyboard 证据已记录,bundle hard budget 已由独立 checker 和 CI step 执行。最新 full review 的 10 个 findings 已全部修复:CategoryPanel 新增/inline edit 字段已补齐 accessible names,新增/编辑均保护 `none`;AssetFilterManager、ReleaseSourceSettingsModal、ReleaseTimeline 和 SearchDemo 的非主操作统一到语义 Button variants;BulkActionToolbar icon-only actions 已本地化命名;ReadmeModal 标题恢复 Radix 自动关联;AIConfigPanel 与 NetworkPanel 表单字段已完成 label/id/aria-labelledby 绑定;RepositoryCard 测试恢复 store fixture;StepperInput 测试改为真实 userEvent Enter keyboard activation。新一轮 review 还指出 SearchBar 同步菜单仍使用手写 role/outside-click/keydown;将用已存在的 DropdownMenu primitives 替换,仅移除 menu state/ref/effect,不改变三种同步模式及确认流程。最新 review 另指出 BulkRestoreModal 三组 standalone Checkbox 缺少 heading association,DiscoveryView 清空 topic 时 refresh effect 被 truthiness guard 跳过,GistEditorModal public Checkbox 缺少 accessible name,SearchDemo 示例卡片需要恢复垂直布局类;这 4 项现已完成最小范围修复,SearchBar 也已迁移到 DropdownMenu primitives。当前提交已通过最终 31 个测试文件/332 个测试、lint、tsc、build、bundle budget、diff check 和安全审计,准备触发下一轮 full review。最新 full review 的 7 个 findings 也已全部完成最小范围修复:ReleaseTimeline 两个 Pre-release switch 命名、NetworkPanel Proxy Type RadioGroup 关联、VectorSearchSettings 三个 toggle 迁移到共享 Switch、Dialog calc 空格、DropdownMenu 子菜单 Portal、Slider 单值类型契约、Slider pointer 测试命中实际 Radix Root,以及 RepositoryEditModal/CategoryEditModal 四个表单控件的本地化 accessible names。修复后 31 个测试文件/329 个测试再次通过,lint、tsc、build、bundle budget、diff check 和安全审计均通过,当前提交待下一轮 full review。最新 full review 的 4 个 findings 也已全部完成最小范围修复:BulkActionToolbar/BulkCategorizeModal/SearchBar/GistView 中性操作统一显式使用 ghost variant,ForkCard 中文来源标签改为“派生自”,CategoryPanel RadioGroup 关联仓库归类 heading,AlertDialog calc width 使用合法 Tailwind 表达式;VectorSearchSettings 的 API/Worker/README 控件也已补齐 label/id 和 visibility Button accessible names。当前 31 个测试文件/329 个测试、lint、tsc、build、bundle budget、diff check 与安全审计均再次通过,准备推送并触发下一轮 full review。最新 full review 的 3 个 findings 也已完成最小范围修复:PopoverContent 改用 bg-popover/text-popover-foreground semantic tokens,BulkCategorizeModal 分类按钮补充 aria-pressed,RepositoryEditModal add-tag icon-only Button 补充本地化 aria-label;VectorSearchSettings 的 API/Auth visibility controls 已使用 ghost 与显式 icon dimensions,index-mode buttons 使用 outline,deploy guide trigger 使用 ghost 与 h-auto。当前 31 个测试文件/329 个测试、lint、tsc、build、bundle budget、diff check 和安全审计均再次通过,准备提交并触发下一轮 full review。最新 full review 的 8 个 findings 也已完成最小范围修复:MarkdownRenderer 行号正向测试断言具体 1/4 文本;DataManagementPanel 的删除确认与导入预览迁移到共享 AlertDialog/Dialog,并关联 GitHub username label/id;SearchBar 高级过滤 chips 补充 aria-pressed;SubscriptionRepoCard 清理重复 class 分支与 dark background;ForkTimeline/ReadmeModal 删除重复 hidden native select 并改用 Radix Select API;CategoryEditModal 对 emoji source 去重、memoize icon grid 并清理重复 dark utilities;DiscoveryView topic refresh 使用 applied value guard;GistCard 操作按钮显式使用 ghost;ReadmeModal 移除重复 Escape/overflow/focus lifecycle 并统一 retry primary tokens;VectorSearchSettings option groups 补充 aria-pressed;SettingsPanel mobile tab ids 使用独立前缀;SyncModeChoiceModal 明确首个 action 的初始 focus。当前 31 个测试文件/329 个测试、lint、tsc、build、bundle budget、diff check 和安全审计均再次通过,准备提交并触发下一轮 full review。最新 full review 的 6 个 findings 也已完成最小范围修复:SearchBar filter toggle 关联 advanced panel 并暴露 aria-expanded;ForkTimeline 底部分页补齐本地化 aria-label;MarkdownRenderer/ReleaseTimeline 中性操作统一 ghost,ReleaseTimeline 省略号改为非交互文本;RepositoryCard、RepositoryEditModal、ForkCard、BilingualMarkdownRenderer、ReadmeModal 和 RepositoryList 补齐 ghost、compact square dimensions 与多行布局覆盖;CategoryPanel、WebDAVPanel 和 ReadmeModal icon-only 操作补齐本地化 aria-label;ui-refactor-summary 与实际移除 hidden native select 的代码一致。当前 31 个测试文件/329 个测试、lint、tsc、build、bundle budget、diff check 和安全审计均再次通过,准备提交并触发下一轮 full review。最新 full review 的 9 个 findings 也已完成最小范围修复:NetworkPanel compact switch/auth/visibility controls 统一 ghost 与紧凑尺寸;SearchBar filter controls 补齐 ghost,sort menu 迁移到 DropdownMenuRadioGroup/RadioItem;ReleaseTimeline 和 ReleaseSourceSettingsModal 的中性 controls 统一 ghost,Pre-release switches 清除默认 padding,items-per-page SelectTrigger 补齐 accessible name;SortAlgorithmTooltip 改用受控 Popover 保留触摸设备说明;BulkRestore 使用 semantic primary tokens;SettingsPanel 清理卸载 tab ref;BulkCategorize/CategoryEdit 清理重复 utilities 并修正保存 hover 对比度;AssetFilter destructive action 清理冲突 dark tokens;BulkAction confirmation tooltip 添加 live region;Discovery request key 同时追踪 topic/platform;ReleaseSource/WebDAV/AIConfig/General 补齐 label/id/aria-labelledby;MenuManagement reorder controls 使用 compact ghost icon buttons。当前 31 个测试文件/329 个测试、lint、tsc、build、bundle budget、diff check 和安全审计均再次通过,准备提交并触发下一轮 full review。 + + +最新 22:03Z full review 的 inline findings 与 review body 中的 duplicate/nitpick 已全部完成最小范围修复:ToastPrimitive.Root 增加 `pointer-events-auto`;RepositoryCard Release 菜单测试直接 focus 已定位的 `menuitem` 再 Enter;ReleaseTimeline 的视图、显示模式和最新版本三组手写 overlay 菜单迁移到共享 Radix DropdownMenu,保留原有 store 更新与分页重置;仓库分组及 ReleaseSourceSettingsModal 仓库列表补齐 `aria-expanded`/`aria-controls`;Tailwind `text-quaternary` 与 `brand.hover` 恢复 0.7/0.9 固定 alpha。其余业务逻辑、store、services、API 和同步流程未改变。 + +本轮门禁结果:31 个测试文件、329 个测试全部通过;`npm run lint`、`npx tsc -b`、`npm run build`、`npm run check:bundle-size`、`git diff --check` 与 `npm audit --omit=dev --audit-level=high` 均通过。最新 build 的 legacy 入口为 2,787.81 kB,独立 checker 实测 2,722.47 KiB,低于 3,000 KiB hard budget;安全审计为 0 vulnerabilities。 + + +8f9a544 后最新 CodeRabbit full review 报告 9 个 actionable comments、1 个 outside-diff comment 和 2 个低风险 nitpick,现已全部修复:AssetFilterManager 的 preset/custom filter 主控与 edit/delete icon actions 恢复紧凑 h-auto/h-6/w-6 尺寸;ForkCard workflow 展开态改用可读的 dark primary 文本;ReleaseCard RPC asset rows 增加 h-auto,Release notes actions 清理冲突 dark tokens 并移除无效 active utility;RepositoryCard inline edit 与 RepositoryList AI 分析触发器改为共享 ghost/icon Button;RepositoryList progress fill 改为 primary 并清理 pause/stop 重复 dark backgrounds;CategoryPanel 保存 hover 恢复 primary 对比度;MenuManagement visibility switch 增加 p-0;StarSyncPanel RadioGroup 关联 scope heading;SearchResultStats 清理重复 border token;githubListsApi 提取 UserListNode 命名类型。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 无诊断;31 个测试文件、329 个测试全部通过;`npm run build` 与 bundle budget 通过,legacy 入口 2,788.32 kB,独立 checker 2,722.97 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +1a04720 后最新 CodeRabbit full review 的 7 个 actionable comments、1 个 outside-diff comment 和 2 个 nitpick 已全部完成:ReleaseCard 头部四个紧凑 controls、SubscriptionRepoCard 三个 icon actions 与 ReadmeModal 目录 rows 均覆盖 shared Button 默认尺寸;CategoryEditModal icon grid 补 `variant="ghost"`/`aria-pressed` 并清理重复 dark utilities;ReleaseSourceSettingsModal 使用 `useId` 生成每个 PaginatedRepoList 实例唯一的 `aria-controls`/region id;ReleaseTimeline 两处 Pre-release 文本可点击切换,数字分页补 `type="button"` 与当前页 `aria-current`;WebDAV active-config RadioGroup 补 accessible name;success badge 和 `.pill-success` 改为 emerald-700;UpdateChecker 将错误提示与检查按钮置于单列容器。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,788.24 kB,独立 checker 2,722.90 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +2de4162 后最新 CodeRabbit full review 的 10 个 actionable comments、1 个 outside-diff comment、1 个 duplicate 和 6 个 nitpick 已全部完成:ReleaseTimeline 仓库分组 Button 改为仅含 phrasing spans;DiscoverySidebar channel 补 `aria-pressed`,DiscoveryView refresh 改为 ghost/icon 并修复 `bg-green-6000`;ForkCard/ForkTimeline/GistEditor/ReleaseCard/ReleaseTimeline/RepositoryCard controls 统一补齐 compact size、ghost/outline variant、分页 `aria-current` 与 dark contrast;SortAlgorithmTooltip 阻止 Popover 自动抢焦点;共享 Select 增加 label overflow clamp、按 side 的 popper offset 和 closed animation;ReadmeModal 清理冗余 fragment;WebDAV/StarSync 等既有 accessible naming 保持有效;githubListsApi 复用 `GitHubListSummary`;移除 deprecated `@types/query-string` 及 lockfile 元数据;CI 删除重复 bundle step;Vite warning limit 调整为 3072 kB。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,790.01 kB,独立 checker 2,724.63 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +3544acf 后最新 CodeRabbit full review 的 6 个 actionable comments 与 2 个 nitpick 已全部完成:MarkdownRenderer 的 Markdown checkbox 恢复原生 `input`,非 checkbox 继续使用共享 Input;RepositoryCard selection 与 ReleaseTimeline repository header 覆盖 Button 默认尺寸;RepositoryEditModal tag 删除、RepositoryList display RadioGroup、SearchBar sort order 补齐 accessible names;McpSettingsPanel 与 NetworkPanel 自定义 switch 重置 `justify-start`/`p-0`;ScrollArea thumb 与 Separator 统一 semantic color tokens;DiscoveryView 删除重复 `isDesktopSafeMode` ternary。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,790.35 kB,独立 checker 2,724.95 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +ff639df 后最新 CodeRabbit full review 的 2 个 actionable comments 与 1 个 duplicate 已全部完成:BackupPanel 备份/恢复按钮补 `h-auto`;NetworkPanel RPC secret visibility 与 DiagnosticLogsPanel close/tabs 明确使用 ghost/icon 语义;DiscoveryView channel-switch effect 跳过 topic 自动 fetch,由 topic/platform effect 独立负责首次和组合变更请求,避免重复请求。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,790.39 kB,独立 checker 2,725.00 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +1a91b5d 后最新 CodeRabbit full review 的 3 个 actionable comments 与 1 个 outside-diff comment 已全部完成:DiscoveryView 在 githubToken 缺失时不再记录 topic/platform applied pair,token 初始化后可正常发起一次请求;McpSettingsPanel 的 switch 补 accessible name,refresh、visibility、token/URL/JSON copy actions 明确使用 ghost/outline variants;RepositoryList AI analysis popup 使用 React `useId`,trigger 补 `aria-expanded`/`aria-controls`,popup 补对应 id。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,790.74 kB,独立 checker 2,725.34 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +57ee771 后最新 CodeRabbit full review 的 6 个 actionable comments 已全部完成:BulkRestoreModal 与 RepositoryList 的 RadioGroupItem 补齐 option text id/aria-labelledby;DiscoveryView tab ref 在卸载时删除 stale entry;ForkTimeline 底部分页与上部分页统一处理 ellipsis、ghost variant、type 和 aria-current;DiagnosticLogsPanel debug/refresh/load-more 使用 ghost;BulkActionToolbar 固定尺寸 actions 使用 ghost/icon size 与 p-0;SearchBar 输入调整为 h-12;MenuManagement switch 加入 justify-start;VectorSearchSettings、SettingsPanel、BackendPanel 和 DataManagementPanel 长内容 Button 加入 h-auto。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,791.81 kB,独立 checker 2,726.38 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + + +a5175b9 后最新 CodeRabbit review 的 3 个 comments 已处理:FilterModal 保存按钮 hover 改为 primary-derived surface 并清理冲突的重复 dark utilities;ReleaseCard RPC 资产行明确使用 ghost variant;ui-refactor-summary 补充说明 29/326 为迁移前历史基线,当前新增回归测试后的最终准确总数为 31 个测试文件、329 个测试,未用错误基线覆盖实际结果。业务逻辑、store、services、API 和同步流程保持不变。 + +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、329 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,791.76 kB,独立 checker 2,726.34 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +debbaa4 后本轮 CodeRabbit 遗留问题已处理:SettingsPanel tabpanel 改为直接使用当前 `displayTab` 的本地化 `aria-label`,避免移动端引用 CSS-hidden 的 desktop tab id;DataManagementPanel 导出选项改为受控 React state,导入文件控件改为可见焦点 ring 的 `peer sr-only`;VectorSearchSettings Auto Detect 改用实例 ref,index-mode buttons 支持纵向布局与长文本换行;GistView、DiagnosticLogsPanel、McpSettingsPanel 补齐输入控件 accessible names;ReleaseTimeline 清除按钮与搜索输入完成 compact spacing;SearchBar 新增 history/suggestion dropdown 的 blur/click 交互测试。未改变任何业务逻辑、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、331 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,792.90 kB,独立 checker 2,727.45 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +4e23650 后最新 CodeRabbit full review 的 11 个 actionable comments 与 5 个 nitpick 已全部处理:BilingualMarkdownRenderer、LoginScreen、DiagnosticLogsPanel 的状态按钮补齐 `aria-pressed`;BulkCategorizeModal 建立 heading/group label 关联;ErrorBoundary 详情按钮补 `aria-expanded`;ReleaseSourceSettingsModal 多行 source row 加入 `h-auto`;AIConfigPanel 与 DataManagementPanel 的 Checkbox 使用显式 id/label 关联,中性 controls 使用 ghost;VectorSearchSettings 的模型来源/索引内容建立 group accessible names,clipboard 命令 await 并捕获失败反馈,搜索阈值改用共享 SliderInput;共享 Slider/SliderInput 强制 thumb label;SettingsPanel 仅在 standalone 分支早退;Toast close 使用固定短 label,Provider/Viewport 提升到 DialogProvider 单例并由 Viewport 统一堆叠。未改变任何业务逻辑、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、331 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,793.55 kB,独立 checker 2,728.08 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +cd5f693 后最新 CodeRabbit full review 的 5 个 inline actionable、3 个 outside-diff functional findings 与 7 个 nitpick 已全部处理:DataManagementPanel import types 正确映射 `uiSettings` 组并清理 username focus tokens;GistView/GistDetailModal/SearchDemo 的选择按钮补齐 `aria-pressed`;SearchBar 星标范围通过 NumberInput id 与 htmlFor 建立 label 关联;ReadmeModal 回顶按钮补 label 与 square sizing;ReleaseTimeline 收起内容从 accessibility tree 移除;DiagnosticLogsPanel event-type popup 暴露 `aria-haspopup`/`aria-expanded`/`aria-controls` 与选项状态;GeneralPanel 恢复 React import;McpSettingsPanel、NetworkPanel 使用 semantic tokens 与 shared switch variants;BackendPanel、AIConfigPanel 恢复状态差异色;alert-dialog 补齐 fade/zoom 动画 utilities;Toast warning/info 使用 semantic tokens。未改变任何业务逻辑、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、331 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,794.55 kB,独立 checker 2,729.06 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +4926cde 后最新 CodeRabbit full review 的 6 个 inline actionable、1 个 outside-diff functional finding 与 5 个 nitpick 已全部处理:SearchBar 主搜索框补持久化本地化 aria-label;CategorySidebar 两个 desktop 分类按钮补 aria-pressed;BulkCategorizeModal 错误块使用 destructive tokens;DiscoveryView topic refresh 增加 request version 与当前 topic/platform guard,阻止 stale response 更新仓库、分页、刷新时间和 loading;FilterModal 关键词删除按钮改为 compact icon;DiagnosticLogsPanel 改为带 label 的 toggle-button group;VectorSearchSettings deploy guide 将 heading 移出 Button 并补 expanded/controls 语义;StepperInput 支持本地化 labels 和 Pointer Events 长按处理;CategoryEditModal/CategoryPanel 共享 validateCategoryName;CategoryPanel、ListsPushIndicator、Toast、MCP、Network 等剩余 raw palette 统一为 semantic tokens;Toast exit animation 改为 controlled open state 并延迟卸载;alert-dialog 动画和前轮修复保持有效。未改变业务流程、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、331 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,794.70 kB,独立 checker 2,729.20 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +b2c21c9 后最终 CodeRabbit full review 的 6 个 actionable 与 4 个 nitpick 已全部处理:DataManagementPanel confirmation content 增加 `confirmation.type` guard;NetworkPanel 两个手写开关迁移到共享 Radix Switch;VectorSearchSettings 剩余 gray border/text/focus/surface utilities 统一为 semantic tokens;MarkdownRenderer code-copy 与 zoom overlay controls 明确 Button type/variant/size/compact dimensions;GistCard/GistView 修正 dark primary text;BackendPanel status icons 继承 pill 状态色;SubscriptionRepoCard GitHub 外链阻止父卡片点击冒泡;SliderInput 让 slider 与 marks 共用 flex-1 轨道列;StepperInput 使用 Pointer Events 并限制主鼠标键、支持本地化 labels;ui-refactor-summary.md 的 Vite warning threshold 修正为 3,072 KiB。未改变业务流程、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、331 个测试全部通过;生产构建与 bundle budget 通过,legacy 入口 2,793.88 kB,独立 checker 2,728.40 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 +38302c1 后 round-19 CodeRabbit full review 的 6 个 actionable、3 个 outside-diff、2 个 duplicate 与 9 个 nitpick 已全部处理:RepositoryList AI action popup 迁移到共享 DropdownMenu;RepositoryCard 以 ref 记录卡片内 outside-dismiss pointerdown 并新增 pointerdown→click 回归测试;StepperInput 使用 render-time latest value ref、主鼠标键过滤和 unmount cleanup;SearchShortcutsHelp 使用 DialogTrigger 与内置 close;DiscoveryView 在无 token 时不记录 applied topic;MCP port 使用 MCP_DEFAULT_PORT 并校验 1–65535;DataManagementPanel 导入类型过滤 bookkeeping keys、取消按钮使用 outline、confirmation content 增加 type guard;BulkRestore 三组 RadioGroup 关联 section headings;SettingsPanel inactive tabs 移除无效 aria-controls;RepositoryEditModal 拒绝真实 `none` 分类、使用 onKeyDown 并清理冲突 dark borders;CategoryPanel RadioGroup 标题使用 label/htmlFor;SyncModeChoiceModal 抽取共享 option class;共享 alert-dialog/dialog 复用 buttonVariants 并补齐 fade/zoom;MarkdownRenderer 控件统一 ghost/icon compact API;GistCard/GistView、BackendPanel、SubscriptionRepoCard、SliderInput 与 UI 走查证据同步修正。未改变业务流程、store、services、API 或同步流程。 +本轮门禁结果:lint 0 errors/0 warnings;TypeScript 无诊断;31 个测试文件、332 个测试全部通过,RepositoryCard outside-dismiss 回归无 act/Unhandled/Warning;生产构建与 bundle budget 通过,legacy 入口 2,794.15 kB,独立 checker 2,728.67 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + +## Round-20 CodeRabbit remediation + +基于 `8520fc1` 后于 2026-08-22 06:29:03Z 完成的 full review,本轮报告 5 个 actionable comments,并包含若干 nitpick/outside-diff 建议。已完成最小范围修复:Discovery topic request 在当前请求失败后清理 `appliedTopicRef` 以允许后续重试;ReadmeModal 在 repository 存在时保持 DialogContent 挂载,由 Radix 负责关闭、焦点恢复与生命周期;DialogContent 增加可选本地化 `closeLabel`、默认 `Close`,并加入 `max-h-[85vh] overflow-y-auto`;SearchShortcutsHelp、ReadmeModal、UpdateChecker 与 DataManagement import preview 传入本地化关闭标签;McpSettingsPanel 使用共享 Radix Switch,端口输入保留编辑中间态,仅对合法 1–65535 值即时更新并在 blur 时回退默认端口;VectorSearchSettings 的 deploy-guide region 常驻并由 `hidden` 控制,dimensions、README max chars 与 Top K 均仅在 blur 时执行默认/范围校验;DataManagementPanel 正确从原始导入数据检测 UI settings,并使用 shared destructive/default Button variants;Discovery stop-analysis 与 sidebar refresh-all 使用 ghost/icon compact controls;RepositoryCard reset 自身 pointerdown 的 stale outside-dismiss 标记;StepperInput 使用 `useLayoutEffect` 同步 latest value ref,保留 pointer repeat cleanup 与 keyboard activation。上述修改未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮修改后的定向回归为 6 个文件/21 个测试全部通过;全量门禁为 31 个测试文件/332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,793.82 kB,独立 bundle checker 为 2,728.35 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本记录仅证明本轮代码修改已通过本地质量门禁;`8520fc1` 对应的 review 当时仍报告 actionable findings,需提交推送并完成后续 CodeRabbit full review,且以明确的 `Actionable comments posted: 0` 作为最终审查证据。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-21 CodeRabbit remediation status + +2026-08-22 07:19:18Z 的 subsequent CodeRabbit full review 针对上一轮推送提交报告 6 条 inline findings,均已完成最小范围修复:GistView sort trigger 增加 `aria-expanded`;RepositoryEditModal 初始化时将历史 `custom_category="none"` 归一化为空字符串,使无关字段可以保存;DiagnosticLogsPanel 的详情视图按钮补充 `aria-pressed`;VectorSearchSettings 在切换 embedding source 时同步 displayed dimensions draft;DropdownMenuContent 与 DropdownMenuSubContent 使用 `bg-popover`/`text-popover-foreground` semantic tokens;ui-refactor-summary 的发布状态措辞改为与当前提交和待审查状态一致。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮修改后的定向回归为 6 个测试文件/21 个测试全部通过;全量门禁为 31 个测试文件/332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,793.84 kB,独立 bundle checker 为 2,728.36 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节记录的是已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-22 CodeRabbit remediation status + +2026-08-22 07:36:40Z 的 subsequent CodeRabbit full review 报告 4 条 inline findings,均已完成最小范围修复:BilingualMarkdownRenderer display controls 使用 `size="sm"` 与紧凑高度;ReadmeModal 目录/字体 icon controls 使用 `size="icon"` 与方形尺寸;DiscoveryView 的时间范围、主题和编程语言 SelectTrigger 补充本地化 accessible names;StarSyncPanel 显式导入 React namespace 以支持 `React.FC` 类型引用。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮修改后的定向回归为 6 个测试文件/21 个测试全部通过;全量门禁为 31 个测试文件/332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.04 kB,独立 bundle checker 为 2,728.56 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-23 CodeRabbit remediation status + +2026-08-22 07:49:32Z 的 subsequent CodeRabbit full review 报告 1 条 inline accessibility finding:BulkCategorizeModal 的分类失败提示已增加 `role="alert"`,确保 `onCategorize` rejection 能被屏幕阅读器播报。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮定向及全量测试均通过:31 个测试文件、332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.05 kB,独立 bundle checker 为 2,728.57 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-24 CodeRabbit remediation status + +2026-08-22 08:06:26Z 的 subsequent CodeRabbit full review 报告 2 条 inline findings,均已完成最小范围修复:DiscoveryView 对 stale topic load-more 请求始终清除该请求建立的 `discoveryIsLoadingMore` 标志,同时保留 current-request guard 保护 stale data writes;SearchBar AI search Button 增加本地化 `aria-label`,确保窄屏隐藏文字时仍有 accessible name。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮完整门禁通过:31 个测试文件、332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.13 kB,独立 bundle checker 为 2,728.65 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-25 CodeRabbit remediation status + +2026-08-22 08:21:18Z 的 subsequent CodeRabbit full review 报告 3 条 inline findings,均已完成最小范围修复:ReleaseSourceSettingsModal visibility toggle 补充 `aria-pressed`;DiagnosticLogsPanel event-type options 使用 neutral `ghost` variant 与 `justify-start`;VectorSearchSettings 的 dimensions、README max chars 和 Top K blur handlers 只接受整数,同时保留原有 fallback 与范围 clamp。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮完整门禁通过:31 个测试文件、332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.18 kB,独立 bundle checker 为 2,728.70 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-26 CodeRabbit remediation status + +2026-08-22 08:36:42Z 的 subsequent CodeRabbit full review 报告 2 条 inline findings,均已完成最小范围修复:bundle checker 现在检查所有 `-legacy-*.js` 资源而非只匹配 `index-legacy-*`;Header 桌面导航补充 `aria-pressed`,移动菜单项补充 `aria-current="page"`。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮完整门禁通过:31 个测试文件、332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.24 kB,独立 bundle checker 为 2,728.75 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-27 CodeRabbit remediation status + +2026-08-22 08:49:06Z 的 subsequent CodeRabbit full review 报告 1 条 inline finding:RepositoryList 的 pause/stop/grid/list controls 已补齐明确紧凑高度,icon-only controls 使用 `size="icon"` 与 `p-0`,保留原有 handlers、labels、variants 和 responsive behavior。未改变 store、services、API、同步流程、AI 行为或数据流。 + +本轮完整门禁通过:31 个测试文件、332 个测试全部通过,测试日志无 React/Radix `Warning`、`act` 或 `Unhandled` 标记。`npm run lint` 通过且 0 errors/0 warnings;`npx tsc -b` 通过;`npm run build` 通过,legacy 入口 2,794.18 kB,独立 bundle checker 为 2,728.70 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 报告 `found 0 vulnerabilities`。 + +本节为已完成本地验证、待提交推送并触发下一次 CodeRabbit full review 的 draft 状态;只有后续 review 明确报告 `Actionable comments posted: 0`,才能确认审查收尾。 + +业务逻辑、store、services、API 和同步流程保持不变。 + +## Round-29 shadcn visual parity remediation status + +本轮针对用户反馈的“组件样式与官方 shadcn/ui 不符、字体字号线条不一致、颜色对比度异常”进行了 presentation-only 收敛。依据官方 shadcn/ui Button/Card 源码与主题文档 [1] [2],全局 canvas、panel、menu、card、field、chip、filter、modal、empty-state 和 navigation helper 统一到 `background`、`foreground`、`card`、`popover`、`muted`、`secondary`、`accent`、`border`、`ring` 等 semantic tokens;移除入口 body 的 legacy surface 色;将系统字体栈改为稳定的系统 sans/mono 栈;Button variants 对齐官方 h-9/h-8/h-10/icon 尺寸、3px focus ring、SVG 尺寸和状态样式;Card 对齐官方 `rounded-xl`、`gap-6`、`py-6`、`text-card-foreground` 与标题/内容层级。 + +页面级修复覆盖 LoginScreen、Header、CategorySidebar、SearchBar、RepositoryCard、SettingsPanel、GistView、DiagnosticLogsPanel、VectorSearchSettings、IncludeKeysToggle、MarkdownRenderer 及相关 shared primitives。主要调整为标准 `h-8/h-9/h-10` 控件密度、`rounded-md` 控件线条、popover/card surfaces、semantic active/hover/focus states、可读的 light/dark foreground pairing,以及 HTTP detail 行的键盘可操作语义;未修改 store、services、API、同步流程、AI 行为或数据流。批量颜色迁移仅针对普通中性色 class,Markdown 图片查看器的专用白色 overlay 保持不变。 + +本地生产预览已验证:开发服务器空白是旧入口运行时观察,生产构建预览可正常挂载;light/dark 登录页均完成实际截图检查。定向回归 4 个测试文件、17 个测试全部通过,日志无 React/Radix `Warning`、`act`、`Unhandled` 或错误标记。完整门禁通过:31 个测试文件、332 个测试全部通过;ESLint 0 errors/0 warnings;`npx tsc -b` 无诊断;生产 build 通过;legacy 入口 `index-legacy-BpAWmd9K.js` 为 2,791.81 kB,独立 bundle checker 为 2,726.38 KiB,低于 3,000 KiB hard budget;`git diff --check` 通过;`npm audit --omit=dev --audit-level=high` 为 0 vulnerabilities。 + +本节记录的是已完成本地验证、尚待整理提交、推送并触发 CodeRabbit full review 的 draft;不能以本节作为 CodeRabbit actionable zero 的证据,只有针对最终推送 commit 的完成报告明确写出 `Actionable comments posted: 0` 后,才能宣称审查收尾完成。 + +[1]: https://ui.shadcn.com/docs/components "shadcn/ui Components" +[2]: https://ui.shadcn.com/docs/theming "shadcn/ui Theming" diff --git a/final-browser-verification.md b/final-browser-verification.md new file mode 100644 index 00000000..32cc16ca --- /dev/null +++ b/final-browser-verification.md @@ -0,0 +1,25 @@ +# 最终浏览器验证 + +验证时间:2026-08-22 UTC(用户时区为 2026-08-23);构建预览地址:`http://127.0.0.1:4174/`;视口:`1440 × 1000`;浏览器使用隔离 profile 与本地 visual-review fixture,不使用真实凭据,也不写入真实业务数据。此次 fixture 至少包含 1 个仓库、1 个 Release(6 个资产行)、1 个 Gist、1 个 Fork 以及 20 个自定义分类,走查通过应用现有同步、订阅、刷新和卡片点击流程加载数据。 + +## 关键几何与交互证据 + +| 验证项 | 实测结果 | 截图/报告 | +|---|---|---| +| README 弹窗 | dialog `x=150, y=75, width=1130, height=850, right=1280, bottom=925`;相对于 client width 1430 和 viewport height 1000 居中,未超出 viewport 安全边界 | `readme-dialog.png`; `metrics.json` 的 `readmeDialog` | +| Release 资产面板 | 展开成功;面板 `x=65, y=511, width=1300, right=1365`,在深色主题中相对页面背景使用明显更浅的语义 surface,6 行资产完整可见 | `release-assets.png`; `metrics.json` 的 `releaseAfterAsset` | +| Fork 每页控件 | 搜索 frame、ForkCard 与 page-size trigger 的右边界均为 `1382`;page-size trigger `x=1302, width=80, right=1382` | `fork-layout.png`; `metrics.json` 的 `forkLayout` | +| Gist 左右容器 | 侧栏 `y=85, right=288`,右侧搜索 toolbar `y=85, right=1398`;顶部基线相同 | `gist-layout.png`; `metrics.json` 的 `gistLayout` | +| Gist hover 可读性 | 真实指针 hover 后条目和计数均为 `rgb(248, 250, 252)`,hover 背景为 `rgba(30, 41, 59, 0.5)` | `round42_dropdown_hover_check.py` 输出 | +| Select 横向稳定 | Release、Gist、Fork 三个下拉在 closed/open/closed 三态中 body 与 root 均保持 `x=0, width=1430, right=1430`,`margin-right=0px`;打开时仅出现 `data-scroll-locked=1` | `round42_dropdown_hover_check.py` 输出 | +| 分类滚动条 | 滚动条元素右边界保持 `x=287`,侧栏卡片右边界 `x=288`;idle/停止后 thumb 透明,滚动态 thumb 为 `rgba(75, 85, 99, 0.8)`,宽度不变化 | `category-idle.png`, `category-scrolling.png`, `category-after-stop.png`; `metrics.json` | + +README、Release、Gist 与 Fork 截图均为 populated state,而不是空状态占位图。Fork 截图包含 `magpie-fork` 卡片;Release 截图包含 `magpie` 的 `Visual Release` 和 6 个资产行;Gist 截图包含真实 fixture 卡片及侧栏计数。分类截图包含 populated repository card;分类滚动 fixture 的 CSS 状态则由同一页面的 idle、scrolling、after-stop 三次截图和 computed style 结果共同验证。 + +## 产物 + +本轮截图和几何报告保存在 `ui-screenshots-round42/`,包括 `readme-dialog.png`、`release-assets.png`、`gist-layout.png`、`gist-dialog.png`、`fork-layout.png`、`category-idle.png`、`category-scrolling.png`、`category-after-stop.png` 与 `metrics.json`。这些文件仅作为本地验证证据,不纳入提交;本记录文件本身为 tracked verification record。 + +## 代码门禁状态 + +本轮最终完整门禁已通过:`npm run lint`、`npx tsc -b`、`npm run test:run`、`npm run build`、`git diff --check` 与 `npm audit --omit=dev --audit-level=high` 全部成功。测试结果为 31 个测试文件、332 个测试全部通过;legacy entry 为 `2713.22 KiB`,低于 `3000 KiB` 限制;生产依赖审计结果为 `0 vulnerabilities`。 diff --git a/index.html b/index.html index 5d3a3d3a..74145862 100644 --- a/index.html +++ b/index.html @@ -7,12 +7,8 @@ GitHub Stars Manager - AI-Powered Repository Management - - - +
diff --git a/package-lock.json b/package-lock.json index e7733a21..8d5a6e2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,9 +6,27 @@ "packages": { "": { "name": "github-stars-manager", - "version": "0.7.4", - "dependencies": { - "@types/query-string": "^6.2.0", + "version": "0.7.5", + "dependencies": { + "@radix-ui/react-alert-dialog": "^1.1.23", + "@radix-ui/react-avatar": "^1.2.6", + "@radix-ui/react-checkbox": "^1.3.11", + "@radix-ui/react-dialog": "^1.1.23", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-radio-group": "^1.4.7", + "@radix-ui/react-scroll-area": "^1.2.18", + "@radix-ui/react-select": "^2.3.7", + "@radix-ui/react-separator": "^1.1.15", + "@radix-ui/react-slider": "^1.4.7", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", + "@radix-ui/react-tabs": "^1.1.21", + "@radix-ui/react-toast": "^1.2.23", + "@radix-ui/react-tooltip": "^1.2.16", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "date-fns": "^3.3.1", "highlight.js": "^11.11.1", "lucide-react": "^0.344.0", @@ -16,11 +34,11 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", - "react-router-dom": "^6.22.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.6.0", "zustand": "^4.5.0" }, "devDependencies": { @@ -42,6 +60,7 @@ "jsdom": "^24.0.0", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", + "tailwindcss-animate": "^1.0.7", "typescript": "^5.5.3", "typescript-eslint": "^8.3.0", "vite": "^8.0.9", @@ -922,6 +941,44 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1008,111 +1065,1148 @@ "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.126.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", + "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz", + "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz", + "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz", + "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz", + "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.7.tgz", + "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz", + "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz", + "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.7.tgz", + "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", + "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.23.tgz", + "integrity": "sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", + "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@jridgewell/remapping/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", "license": "MIT", - "peer": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", "license": "MIT", - "peer": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", "license": "MIT", - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@napi-rs/wasm-runtime": { + "node_modules/@radix-ui/react-use-rect": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", "license": "MIT", - "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "@radix-ui/rect": "1.1.3" }, "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@oxc-project/types": { - "version": "0.126.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", - "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", - "dev": true, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@remix-run/router": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", - "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", + "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", "license": "MIT", - "engines": { - "node": ">=14.0.0" + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, + "node_modules/@radix-ui/rect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", + "license": "MIT" + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.16", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", @@ -1906,12 +3000,6 @@ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, - "node_modules/@types/query-string": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@types/query-string/-/query-string-6.2.0.tgz", - "integrity": "sha512-dnYqKg7eZ+t7ZhCuBtwLxjqON8yXr27hiu3zXfPqxfJSbWUZNwwISE0BJUxghlcKsk4lZSp7bdFSJBJVNWBfmA==", - "license": "MIT" - }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", @@ -1926,7 +3014,7 @@ "version": "18.3.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" @@ -3900,6 +4988,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -4376,6 +5476,27 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4759,6 +5880,12 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -5476,6 +6603,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -7944,9 +9080,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -8308,9 +9444,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { @@ -8328,7 +9464,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8645,36 +9781,73 @@ "react": ">=18" } }, - "node_modules/react-router": { - "version": "6.30.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", - "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.2" + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" }, "engines": { - "node": ">=14.0.0" + "node": ">=10" }, "peerDependencies": { - "react": ">=16.8" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/react-router-dom": { - "version": "6.30.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", - "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.2", - "react-router": "6.30.3" + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/read-cache": { @@ -9635,6 +10808,16 @@ "dev": true, "license": "MIT" }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", @@ -9673,6 +10856,16 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/terser": { "version": "5.46.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", @@ -9895,7 +11088,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -10367,6 +11559,49 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", diff --git a/package.json b/package.json index 1002b4ad..12643ae6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "vite", "prebuild": "npm run sync-version", - "build": "vite build", + "build": "vite build && npm run check:bundle-size", + "check:bundle-size": "node scripts/check-bundle-size.cjs", "lint": "eslint .", "preview": "vite preview", "test": "vitest", @@ -25,7 +26,25 @@ "build:all": "npm run build && npm run build:server" }, "dependencies": { - "@types/query-string": "^6.2.0", + "@radix-ui/react-alert-dialog": "^1.1.23", + "@radix-ui/react-avatar": "^1.2.6", + "@radix-ui/react-checkbox": "^1.3.11", + "@radix-ui/react-dialog": "^1.1.23", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-radio-group": "^1.4.7", + "@radix-ui/react-scroll-area": "^1.2.18", + "@radix-ui/react-select": "^2.3.7", + "@radix-ui/react-separator": "^1.1.15", + "@radix-ui/react-slider": "^1.4.7", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", + "@radix-ui/react-tabs": "^1.1.21", + "@radix-ui/react-toast": "^1.2.23", + "@radix-ui/react-tooltip": "^1.2.16", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "date-fns": "^3.3.1", "highlight.js": "^11.11.1", "lucide-react": "^0.344.0", @@ -33,11 +52,11 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", - "react-router-dom": "^6.22.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.6.0", "zustand": "^4.5.0" }, "devDependencies": { @@ -59,6 +78,7 @@ "jsdom": "^24.0.0", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", + "tailwindcss-animate": "^1.0.7", "typescript": "^5.5.3", "typescript-eslint": "^8.3.0", "vite": "^8.0.9", diff --git a/public/fonts/inter-latin-ext.woff2 b/public/fonts/inter-latin-ext.woff2 deleted file mode 100644 index 479d010d..00000000 Binary files a/public/fonts/inter-latin-ext.woff2 and /dev/null differ diff --git a/public/fonts/inter-latin.woff2 b/public/fonts/inter-latin.woff2 deleted file mode 100644 index d15208de..00000000 Binary files a/public/fonts/inter-latin.woff2 and /dev/null differ diff --git a/public/fonts/inter.css b/public/fonts/inter.css deleted file mode 100644 index 77094f29..00000000 --- a/public/fonts/inter.css +++ /dev/null @@ -1,53 +0,0 @@ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('./inter-latin-ext.woff2') format('woff2'); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('./inter-latin.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('./inter-latin-ext.woff2') format('woff2'); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('./inter-latin.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('./inter-latin-ext.woff2') format('woff2'); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('./inter-latin.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/scripts/check-bundle-size.cjs b/scripts/check-bundle-size.cjs new file mode 100644 index 00000000..a3cdca79 --- /dev/null +++ b/scripts/check-bundle-size.cjs @@ -0,0 +1,34 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const DIST_ASSETS_DIR = path.resolve('dist/assets'); +const MAX_BUNDLE_KIB = 3000; +const LEGACY_ASSET_PATTERN = /-legacy-.*\.js$/; + +if (!fs.existsSync(DIST_ASSETS_DIR)) { + console.error(`Bundle budget check failed: ${DIST_ASSETS_DIR} does not exist.`); + process.exit(1); +} + +const legacyEntries = fs + .readdirSync(DIST_ASSETS_DIR) + .filter((fileName) => LEGACY_ASSET_PATTERN.test(fileName)); + +if (legacyEntries.length === 0) { + console.error('Bundle budget check failed: no legacy entry was generated in dist/assets.'); + process.exit(1); +} + +const largestEntry = legacyEntries + .map((fileName) => { + const filePath = path.join(DIST_ASSETS_DIR, fileName); + return { fileName, sizeKiB: fs.statSync(filePath).size / 1024 }; + }) + .sort((left, right) => right.sizeKiB - left.sizeKiB)[0]; + +console.log(`Bundle budget: ${largestEntry.fileName} = ${largestEntry.sizeKiB.toFixed(2)} KiB (limit ${MAX_BUNDLE_KIB} KiB)`); + +if (largestEntry.sizeKiB > MAX_BUNDLE_KIB) { + console.error(`Bundle budget exceeded by ${(largestEntry.sizeKiB - MAX_BUNDLE_KIB).toFixed(2)} KiB.`); + process.exit(1); +} diff --git a/shadcn-visual-baseline.md b/shadcn-visual-baseline.md new file mode 100644 index 00000000..16c5e4e5 --- /dev/null +++ b/shadcn-visual-baseline.md @@ -0,0 +1,16 @@ +# Official shadcn/ui visual baseline + +Research date: 2026-08-22. + +The official shadcn/ui documentation describes a token-first system: components should use semantic pairs such as `background`/`foreground`, `card`/`card-foreground`, `popover`/`popover-foreground`, `primary`/`primary-foreground`, `secondary`/`secondary-foreground`, `muted`/`muted-foreground`, `accent`/`accent-foreground`, `destructive`, `border`, `input`, and `ring`. Dark mode should override the same semantic tokens under `.dark`, rather than requiring each component to duplicate hard-coded light/dark colors. + +The official Button API uses explicit `variant` and `size` props. Its documented sizes include default, small, large, and icon; icon-only controls should use the icon size rather than compensating with arbitrary padding. The official Card composition uses Card, CardHeader, CardTitle, CardDescription, CardContent, and CardFooter, with a shared radius/spacing scale and `card` surface tokens. + +For this audit, the acceptance baseline is: the global body uses `bg-background text-foreground`; elevated surfaces use `bg-card text-card-foreground`; floating menus/dialog surfaces use `bg-popover text-popover-foreground`; controls use the shared Button/Input/Select/Switch variants and size props; borders use `border-border`; focus uses `ring-ring`; and component-local raw `bg-white`, `text-gray-*`, `border-gray-*`, oversized `rounded-*`, or duplicate `dark:*` overrides are removed unless they are semantically required. + +References: + +1. [shadcn/ui Components](https://ui.shadcn.com/docs/components) +2. [shadcn/ui Theming](https://ui.shadcn.com/docs/theming) +3. [shadcn/ui Button](https://ui.shadcn.com/docs/components/button) +4. [shadcn/ui Card](https://ui.shadcn.com/docs/components/card) diff --git a/src/App.tsx b/src/App.tsx index 7eddee6c..bed35134 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -250,8 +250,8 @@ function App() { // Show loading state while store is hydrating to ensure correct theme is applied if (!hasHydrated) { return ( -
-
+
+
Loading...
@@ -263,7 +263,7 @@ function App() { } return ( -
+
diff --git a/src/components/AssetFilterManager.tsx b/src/components/AssetFilterManager.tsx index e674e1d1..59be33d8 100644 --- a/src/components/AssetFilterManager.tsx +++ b/src/components/AssetFilterManager.tsx @@ -4,6 +4,7 @@ import { useAppStore } from '../store/useAppStore'; import { FilterModal } from './FilterModal'; import { AssetFilter } from '../types'; import { useDialog } from '../hooks/useDialog'; +import { Button } from './ui/button'; // 图标映射 const ICON_MAP: Record = { @@ -98,55 +99,57 @@ export const AssetFilterManager: React.FC = ({
{/* Compact Header with Toggle */}
- +
{selectedFilters.length > 0 && ( - + )} - +
@@ -160,7 +163,7 @@ export const AssetFilterManager: React.FC = ({ {/* Preset Filters */} {presetFilters.length > 0 && (
-

+

{t('预设筛选器', 'Preset Filters')}

@@ -172,31 +175,33 @@ export const AssetFilterManager: React.FC = ({ key={preset.id} className={`group flex items-center space-x-1.5 px-3 py-1.5 rounded-lg border text-xs transition-colors ${ isSelected - ? 'bg-gray-900 border-transparent text-white dark:bg-white/[0.12] dark:border-white/[0.2] dark:text-white font-medium' - : 'bg-white border-black/[0.06] text-gray-700 dark:bg-white/[0.04] dark:border-white/[0.04] dark:text-text-secondary hover:bg-gray-50 hover:text-gray-900 dark:hover:bg-white/[0.08] dark:hover:text-text-primary' + ? 'bg-gray-900 border-transparent text-white dark:bg-card/[0.12] dark:border-border/[0.2] dark:text-foreground font-medium' + : 'bg-card border-border text-muted-foreground dark:bg-muted/40 dark:border-border dark:text-muted-foreground hover:bg-accent hover:text-foreground dark:hover:bg-accent dark:hover:text-foreground' }`} > - +
- +
); @@ -208,7 +213,7 @@ export const AssetFilterManager: React.FC = ({ {/* Custom Filters */} {customFilters.length > 0 && (
-

+

{t('自定义筛选器', 'Custom Filters')}

@@ -217,13 +222,14 @@ export const AssetFilterManager: React.FC = ({ key={filter.id} className={`group flex items-center space-x-2 px-3 py-2 rounded-lg border transition-colors ${ selectedFilters.includes(filter.id) - ? 'bg-gray-900 border-transparent text-white dark:bg-white/[0.12] dark:border-white/[0.2] dark:text-white font-medium' - : 'bg-light-surface border-black/[0.06] text-gray-900 dark:bg-white/[0.04] dark:border-white/[0.04] dark:text-text-secondary hover:bg-gray-200 dark:hover:bg-gray-600' + ? 'bg-gray-900 border-transparent text-white dark:bg-card/[0.12] dark:border-border/[0.2] dark:text-foreground font-medium' + : 'bg-muted border-border text-foreground dark:bg-muted/40 dark:border-border dark:text-muted-foreground hover:bg-accent dark:hover:bg-accent' }`} > - +
- - +
))} @@ -262,8 +270,8 @@ export const AssetFilterManager: React.FC = ({ )} {presetFilters.length === 0 && customFilters.length === 0 && ( -
-

+

+

{t('暂无过滤器,点击"新建"创建', 'No filters, click "New" to create')}

diff --git a/src/components/BackToTop.tsx b/src/components/BackToTop.tsx index a6998bec..c09c76c2 100644 --- a/src/components/BackToTop.tsx +++ b/src/components/BackToTop.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { ArrowUp } from 'lucide-react'; import { useAppStore } from '../store/useAppStore'; +import { Button } from './ui/button'; export const BackToTop: React.FC = () => { const [isVisible, setIsVisible] = useState(false); @@ -62,20 +63,22 @@ export const BackToTop: React.FC = () => { }, [isVisible, triggerBounce]); return ( - + + ); }; diff --git a/src/components/BilingualMarkdownRenderer.tsx b/src/components/BilingualMarkdownRenderer.tsx index 24e7fe1c..6bb2f1c2 100644 --- a/src/components/BilingualMarkdownRenderer.tsx +++ b/src/components/BilingualMarkdownRenderer.tsx @@ -1,3 +1,4 @@ +import { Button } from './ui/button'; import { memo, useState, useRef, useCallback, useEffect, useImperativeHandle, forwardRef } from 'react'; import MarkdownRenderer from './MarkdownRenderer'; import { @@ -233,7 +234,7 @@ const BilingualMarkdownRenderer = forwardRef) @@ -344,7 +345,7 @@ const BilingualMarkdownRenderer = forwardRef{BILINGUAL_MODE_CSS} {!isControlled && ( -
+
{[ { mode: 'original' as DisplayMode, @@ -365,22 +366,25 @@ const BilingualMarkdownRenderer = forwardRef handleModeChange(mode)} disabled={disabled} - className={`flex items-center gap-1 px-2 py-1 text-xs rounded-md transition-colors ${ + aria-pressed={active} + className={`flex h-7 items-center gap-1 px-2 py-1 text-xs rounded-md transition-colors ${ active - ? 'bg-brand-indigo/20 text-brand-violet dark:bg-brand-indigo/10' + ? 'bg-primary/20 text-primary dark:bg-primary/10' : disabled - ? 'text-gray-300 dark:text-text-quaternary cursor-not-allowed' - : 'text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-light-surface dark:hover:bg-white/5' + ? 'text-muted-foreground dark:text-muted-foreground/70 cursor-not-allowed' + : 'text-muted-foreground hover:text-muted-foreground dark:hover:text-muted-foreground hover:bg-muted dark:hover:bg-card' }`} title={label} > {label} - + ); })}
@@ -397,7 +401,7 @@ const BilingualMarkdownRenderer = forwardRef {status === 'translating' && ( -
+
{language === 'zh' ? '翻译中...' : 'Translating...'} diff --git a/src/components/BulkActionToolbar.tsx b/src/components/BulkActionToolbar.tsx index 1dfebaa4..52ad0c77 100644 --- a/src/components/BulkActionToolbar.tsx +++ b/src/components/BulkActionToolbar.tsx @@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react'; import { X, Star, FolderOpen, Bot, Bell, BellOff, CheckSquare, Square, Loader2, Lock, Unlock, RotateCcw } from 'lucide-react'; import { Repository } from '../types'; import { useAppStore } from '../store/useAppStore'; +import { Button } from './ui/button'; interface BulkActionToolbarProps { selectedCount: number; @@ -167,7 +168,7 @@ export const BulkActionToolbar: React.FC = ({ return (
= ({ {/* Selection Info */}
- + {t(`已选择 ${selectedCount} 个`, `Selected ${selectedCount}`)}
{/* Quick Actions */}
- - +
{/* Action Buttons */}
- + - + - + - + - + - + - + - + -
+
- +
@@ -353,7 +389,9 @@ export const BulkActionToolbar: React.FC = ({ {/* 弱气泡提示 */} {tooltip && (
= ({ }} > {tooltip.message} -
+
)}
diff --git a/src/components/BulkCategorizeModal.tsx b/src/components/BulkCategorizeModal.tsx index e0b910e1..1efc37f1 100644 --- a/src/components/BulkCategorizeModal.tsx +++ b/src/components/BulkCategorizeModal.tsx @@ -1,4 +1,5 @@ -import React, { useState, useEffect } from 'react'; +import { Button } from './ui/button'; +import React, { useId, useState, useEffect } from 'react'; import { Check } from 'lucide-react'; import { Modal } from './Modal'; import { Repository } from '../types'; @@ -20,6 +21,7 @@ export const BulkCategorizeModal: React.FC = ({ const { customCategories, hiddenDefaultCategoryIds, defaultCategoryOverrides, language } = useAppStore(); const [selectedCategory, setSelectedCategory] = useState(null); const [isProcessing, setIsProcessing] = useState(false); + const categorySelectionLabelId = useId(); const allCategories = getAllCategories(customCategories, language, hiddenDefaultCategoryIds, defaultCategoryOverrides); @@ -58,68 +60,74 @@ export const BulkCategorizeModal: React.FC = ({ title={t('批量分类', 'Bulk Categorize')} >
-

+

{t(`将为 ${repositories.length} 个仓库设置分类:`, `Will set category for ${repositories.length} repositories:`)}

-