Files
offerpai_browser_plug/.kiro/steering/project-guidelines.md
T
2026-07-30 15:29:24 +08:00

117 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
inclusion: always
---
# 项目开发规范与注意事项
## 技术栈
- Plasmo + React + TypeScript
- 样式:SCSS
## 项目结构
```
src/
├── background/
│ └── index.ts # 插件后台脚本(Service Worker
├── components/
│ ├── SidebarPanel.tsx # 侧边栏面板组件(主操作界面,自动填写入口)
│ └── SidebarPanel.scss # 侧边栏样式
├── contents/
│ └── sidebar.tsx # Content Script 入口(注入侧边栏到页面)
├── lib/
│ ├── types.ts # 所有共享类型定义(简历接口、表单标签、匹配字段、UI组件库配置)
│ ├── constants.ts # 常量数据(JOB_FORM_LABELS标签数组、TEST_FILL_DATA测试数据、UI_LIB_PICKER_CONFIGS组件库配置、SPECIAL_LABEL_PLACEHOLDERS特殊标签修正配置)
│ ├── dom.ts # DOM工具(extractDomStructure提取DOM树、detectPageLanguage语言检测、isJobApplicationForm表单页判断、buildSelector选择器生成)
│ ├── formMatcher.ts # 表单字段匹配(matchFormFields在DOM中匹配标签对应的input、findNearestInput查找input、fixSpecialLabelInput特殊标签修正)
│ ├── pickerDetector.ts # 选择器识别(detectPickerField检测字段是否为选择器类型,通过UI组件库类名/提示文字/主动点击DOM差异对比三种方式)
│ ├── pickerFill.ts # 选择器选项匹配与点击(fuzzyMatchScore模糊匹配、clickBestOptionInDropdown弹出层选项点击、findAndClickOptionInVisiblePopups全局弹出层搜索)
│ ├── datePicker.ts # 日期选择器填写(fillDatePicker日期填写、tryClickMonth月份点击、tryClickDay日期点击)
│ ├── autofill.ts # 自动填写核心(delay/forceSetValue工具函数、fillMatchedField字段填写入口、fillPickerField选择器填写含DOM差异对比、closePopup弹窗关闭、fillAllFields批量填写)
│ ├── resumeUpload.ts # 简历上传(detectAndUploadResume检测上传按钮并从OSS下载注入文件)
│ └── api.ts # API接口模块
└── assets/
├── icon.png # 插件图标
└── icon1.png # 备用图标
```
## 编码规范
- 页面结构和ts的常量变量和方法都要加中文注释
- 重要逻辑加【注意】标记防止误删(如fillPickerField的DOM差异对比逻辑)
## 注意事项
- 项目接口有Java后端和Python AI后端两种,如果是联调加入新接口,一定要确认有没有说是哪个后端的
- fillPickerField中的DOM差异对比弹出层检测是核心机制,不要简化或删除
- detectPickerField的方式3(主动点击检测DOM变化)是自建组件选择器识别的关键,不要删除
- UI_LIB_PICKER_CONFIGS里不要加容易误匹配的配置(如之前Beisen Phoenix被误匹配到新东方自建网站)
## 填充操作核心规范
- **所有字段填充必须走 `fillMatchedField` 统一入口**,禁止在外部自行编写填充逻辑
- **选择器类型字段(下拉、日期、月份、级联等)绝对不能用 `forceSetValue` 直接写值**,这类 input 通常是受控组件,直接写值无效
- **选择器类型检测必须走 `detectPickerField` 统一入口**pickerDetector.ts),禁止在外部自行判断字段是否为选择器
- detectPickerField 内部已封装三种检测方式:UI组件库类名匹配 → 提示文字检测 → 主动点击DOM差异对比
- 不要自行通过 readonly、class 等属性简单判断是否为选择器,这样会漏判
- 时间字段(开始时间/结束时间/起止时间)如果没有找到 `placeholder="年"/"月"` 的下拉输入框,必须标记 `isPicker=true``fillMatchedField``fillPickerField``fillDatePicker``tryFillMonthPanel` 已封装的完整链路
- **禁止在新增代码中自行编写选择器展开、月份点击、日期导航等操作逻辑**,必须先查看项目中已封装的方法并直接引用:
- 选择器类型检测 → `pickerDetector.ts`detectPickerField
- 日期/月份选择器操作 → `datePicker.ts`fillDatePicker / tryFillMonthPanel / navigateToYearMonth / clickDayCell
- 下拉选项匹配与点击 → `pickerFill.ts`clickBestOptionInDropdown / findAndClickOptionInVisiblePopups
- 选择器展开与DOM差异检测 → `autofill.ts` 中的 `fillPickerField`
- 单选按钮点击 → `autofill.ts` 中的 `fillRadioField`
- 搜索型选择器 → `autofill.ts` 中的 `fillSearchPickerField`
- 年月下拉选择器 → `autofill.ts` 中的 `fillYearMonthPicker`
- `datePicker.ts` 中的按钮探测逻辑(detectNavButtons:逐个点击观察年月变化)是通用日期选择器适配的核心,不要简化或删除
## 经历段落定位核心规范
- `experienceSection.ts` 中的段落容器定位逻辑(DOM 差异对比 + 特征重建)是5大经历区域填写的核心,**不可擅自修改或简化**
- 核心原理:
1. 点击添加按钮前后,用 `snapshotElementsInRange` / `diffSnapshots` / `findTopLevelNewElements`dom.ts)对比 DOM 快照差异
2. 找到新增的顶层容器(一段经历的完整 DOM),记住它的 tag + className 前缀作为段容器特征
3. 同时保存新增段容器的 DOM 引用(`lastNewContainerEl`
4. 所有段添加完后,从 `lastNewContainerEl.parentElement` 出发,在父级 children 中匹配所有同特征兄弟容器
5. 按 DOM 顺序重建 `segmentRanges` 数组,确保每段经历的 containerElement 精确对应
- **禁止**用 input 差集(`beforeInputs`)来定位段容器——React 重渲染会导致原始段的 input 节点被重建,差集不可靠
- **禁止**依赖 `titleElement` 存活状态来做重建——React 重渲染可能使 titleElement 脱离 DOM
- 对于初始有1段且不需要添加的经历:走 `detectFirstSegment` 的 fallback 逻辑(无同级兄弟时 containerElement 可能为 null`matchFormFieldsInRange` 有 fallback 用 startElement/endElement 范围搜索)
## 下拉选择器弹出层搜索核心规范
- **弹出层检测不依赖任何固定类名**,全部走 DOM 差异对比(点击前后全页面快照对比新增/新可见元素)
- **禁止使用 POPUP_SELECTORS 或任何固定 CSS 选择器来检测弹出层**,必须走全页面可见性差异对比(`offsetHeight > 0 && offsetWidth > 0`
- **pickerDetector.ts 方式3(主动点击检测)的核心约束**:
- 检测到弹出层后 **禁止关闭弹出层**(不调用 dismissPopup
- 将弹出层 DOM 引用保存到 `field.pickerDropdownElement`
- fillPickerField 步骤0 直接在已有弹出层内搜索,避免二次点击触发 toggle 关闭
- 弹出层由用户选择后自动关闭,无需手动干预
- **选项搜索必须限定在检测到的弹出层元素内**,禁止全页面搜:
- `findAndClickOptionInVisiblePopups(fillValue, labelText, popupEl?)` 没有 `popupEl` 时直接返回 false,不做全页面搜
- `fillPickerField` 步骤0 用 `field.pickerDropdownElement`pickerDetector 保存的引用)在弹出层内搜索
- `fillPickerField` 步骤4 在 DOM 差异检测到的 `dropdownEl` 内搜索
- `fillSearchPickerField` 轮询检测到的 `newPopupEl` 传给 `clickBestOptionInDropdown``findAndClickOptionInVisiblePopups`
- **fillPickerField 步骤0**pickerDetector 可能已展开弹出层(toggle 冲突问题),所以在步骤1之前先尝试在当前页面搜索匹配选项,避免二次点击关闭弹出层。**不可删除此步骤**
- **isAsyncDropdown 字段的特殊处理**FormLabelItem.isAsyncDropdown = true):
- 接口异步下拉字段(如学校、专业)输入值后需轮询等待接口返回数据(最多3秒)
- 弹出层检测阈值为 ≥1 个可见子元素(普通字段为 ≥2)
- 检测到弹出层后优先用 `clickBestOptionInDropdown(newPopupEl)` 在弹出层内直接做文字匹配
- 禁止对 isAsyncDropdown 字段走 `tryClickDropdownListItem` 全页面搜(选项数可能为1,全页面搜会被其他同规格元素干扰)
- `pickerFill.ts``groupBySpec` / `findVisibleListGroups` 的同规格标签组阈值为 ≥2(不可改回 ≥3,否则只有2个选项的下拉列表会漏匹配)
- `clickBestOptionInDropdown` 是在指定弹出层 DOM 内递归搜所有可见叶子文字节点做模糊匹配的核心方法,**不可简化其递归逻辑**
## 延时调用规范
- **项目中所有延时调用必须且只能使用 `src/utils/delay.ts` 中导出的 `delay` 函数**
- 禁止在任何文件中自行编写 `setTimeout` / `new Promise(resolve => setTimeout(...))` 等延时逻辑
- `delay` 函数接收延时等级参数,统一管理延时时长:
- `delay("low")` — 低延时(微等待,如点击后极短暂停)
- `delay("mid")` — 中延时(等待 DOM 更新、弹出层渲染等)
- `delay("high")` — 高延时(等待接口返回、搜索结果、动画完成等)
- `delay("max")` — 特殊超长延时(谨慎使用)
- 具体毫秒数只在 `src/utils/delay.ts` 中统一设置,调用方只使用等级名称
- 如需调整全局延时策略,只修改 `delay.ts` 中的 `DELAY_MS` 常量即可