Skip to content

Commit af8b113

Browse files
committed
refactor: consolidate all runtime code under src/ (popup/debug/detect/background/shared/i18n), toggle into shared, domain-rules-manager into background; all references rewritten; verified via real Chrome load (zero 404/js errors)
1 parent 95ba8d0 commit af8b113

82 files changed

Lines changed: 180 additions & 193 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,9 @@ jobs:
5959
VERSION=$(jq -r .version manifest.json)
6060
STAGE="MultiLangSwitcher-v${VERSION}"
6161
mkdir "$STAGE"
62-
cp manifest.json LICENSE \
63-
background.js popup.html popup.js \
64-
debug.html debug-ui.js debug-headers.js \
65-
detect.html detect.js detect-ai.js \
66-
toggle.js toggle.css \
67-
domain-rules-manager.js domain-rules.json \
68-
bootstrap.purged.css \
69-
"$STAGE/"
70-
cp -r background popup debug detect _locales shared i18n images fonts "$STAGE/"
71-
find "$STAGE/shared/vendor" -name "*.map" -delete 2>/dev/null || true
62+
cp manifest.json LICENSE domain-rules.json bootstrap.purged.css "$STAGE/"
63+
cp -r src _locales images fonts "$STAGE/"
64+
find "$STAGE/src/shared/vendor" -name "*.map" -delete 2>/dev/null || true
7265
zip -r "${STAGE}.zip" "$STAGE"
7366
sha256sum "${STAGE}.zip" | awk '{print $1}' > digest.txt
7467
printf '%s\n' "$GITHUB_SHA" > source-sha.txt

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ Features
7575
* **Theme Switching**: Support for light/dark theme switching
7676
* **Update Checker**: Automatically checks GitHub Releases for the latest version information
7777
* **Reset Function**: One-click reset of the Accept-Language request header
78-
* **Detection Page**: `detect.html` for verifying request header modifications, which detects:
78+
* **Detection Page**: `src/detect/detect.html` for verifying request header modifications, which detects:
7979
- `Accept-Language` request headers
8080
- JavaScript language preferences (`navigator.language`, `navigator.languages`)
8181
- Internationalization API (Intl) information
8282
- WebRTC local IP leakage
8383
- Canvas, WebGL, and AudioContext fingerprinting information
8484
- AI Diagnosis panel with OpenAI-compatible providers, multi-turn chat, per-provider local config, Markdown export, and message copy actions
85-
* **Debugging Tools**: `debug.html` provides debugging and diagnostic features, including:
85+
* **Debugging Tools**: `src/debug/debug.html` provides debugging and diagnostic features, including:
8686
- View `declarativeNetRequest` dynamic rule details
8787
- Multi-endpoint request header testing
8888
- Custom `Accept-Language` strings (e.g., `en-US,en;q=0.9,zh-CN;q=0.8`)
@@ -133,7 +133,7 @@ Usage
133133

134134
### AI Diagnosis Notes
135135

136-
The `detect.html` page now includes an AI diagnosis panel. It sends the current structured detection snapshot to an OpenAI-compatible model and turns the raw data into user-friendly explanations.
136+
The `src/detect/detect.html` page now includes an AI diagnosis panel. It sends the current structured detection snapshot to an OpenAI-compatible model and turns the raw data into user-friendly explanations.
137137

138138
Tips:
139139

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838
}
3939
},
4040
"files": {
41-
"ignore": ["bootstrap.min.css", "bootstrap.purged.css", "shared/vendor/**", ".codegraph/**", "coverage/**"]
41+
"ignore": ["bootstrap.min.css", "bootstrap.purged.css", "src/shared/vendor/**", ".codegraph/**", "coverage/**"]
4242
}
4343
}

docs/Project_Structure.md

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,80 @@
11
# 项目结构
22

3-
> 2026-08 重构后:全仓 ES Modules,无构建步骤,浏览器/SW 原生加载。
3+
> 2026-08 重构后:全仓 ES Modules,无构建步骤,浏览器/SW 原生加载。代码统一收拢在 `src/`
44
55
```
66
MultiLangSwitcher/
77
├── LICENSE - 许可证文件
88
├── README.md - 项目说明文档
9-
├── package.json - 工程基线(biome/vitest,private,仅 devDependencies)
10-
├── biome.json - lint + format 配置(tab 缩进、双引号)
11-
├── _locales/ - 扩展描述的国际化目录(chrome.i18n,仅 manifest 元数据)
12-
│ ├── en/
13-
│ └── zh/
14-
├── docs/ - 项目文档目录
15-
├── scripts/ - CI 校验脚本
16-
│ ├── check-syntax.mjs - 全部 js 的 node --check
17-
│ └── validate-manifest.mjs - manifest + _locales 校验
18-
├── tests/ - vitest 测试(纯逻辑 + 失败路径)
19-
│ └── helpers/global-loader.js - extractFunction(提取自包含纯函数)
20-
├── shared/ - 共享 ESM 模块
21-
│ ├── message-types.js - 消息协议常量(MessageTypes)
22-
│ ├── storage-keys.js - 存储键常量(STORAGE_KEYS / LOCAL_STORAGE_KEYS)
23-
│ ├── shared-utils.js - 通用工具(日志、语言检测、兜底翻译)
24-
│ ├── shared-i18n-base.js - BaseI18n 基类(字典由子类静态注入)
25-
│ ├── shared-actions.js - requestBackground 信封 + resetAcceptLanguage
26-
│ ├── shared-update-checker.js - UpdateChecker(版本比较/缓存)
27-
│ ├── shared-language-options.js - 语言选项列表
28-
│ ├── header-check-utils.js - 请求头检查工具
29-
│ ├── shared-resource-manager.js - 资源管理器
30-
│ ├── ai-provider-presets.js - AI 服务商预设
31-
│ ├── ai-chat-client.js - OpenAI 兼容聊天客户端
32-
│ ├── copy-button.js - 复制按钮组件(+ copy-button.css)
33-
│ ├── md5.js - MD5 哈希
34-
│ ├── theme-init.js - 主题初始化(classic script,预渲染同步执行)
35-
│ └── vendor/ - 第三方库(marked / DOMPurify,classic script)
36-
├── i18n/ - 运行时 i18n(每组件单文件双语字典 + 实例模块)
37-
│ ├── popup-dict.js / popup-i18n.js
38-
│ ├── debug-dict.js / debug-i18n.js
39-
│ ├── detect-dict.js / detect-i18n.js
40-
│ └── background-dict.js / background-i18n.js (Service Worker)
41-
├── fonts/ images/ - 静态资源
42-
├── manifest.json - MV3 清单(background 为 module SW)
43-
├── popup.html / popup.js - 弹窗页(单 module 入口)
44-
│ └── popup/ - 弹窗模块(shared/ui-sync/language-apply/update-check)
45-
├── debug.html / debug-ui.js - 调试页(单 module 入口)
46-
│ └── debug/ - 调试模块(safe-dom/log-panel/rules-view/header-test/cache-mgmt/custom-language/rules-fix/diagnostics/auto-switch/domain-rules)
47-
├── debug-headers.js - 调试页控制台工具(window.debugHeaders)
48-
├── detect.html / detect.js - 检测页采集编排(DetectPageContext 为 AI 侧唯一读接口)
49-
│ └── detect/ - 检测模块(shared/collectors/renderers/snapshot/ai-shared/ai-config/ai-ui/ai-session)
50-
├── detect-ai.js - 检测页 AI 诊断(单 module 入口;经 CustomEvent 感知快照生命周期)
51-
├── toggle.js / toggle.css - 语言/主题切换(ESM,被各页面入口 side-effect 导入)
52-
├── domain-rules-manager.js - 域名规则管理(LRU 缓存;customDomainRules 扩展点)
53-
├── domain-rules.json - 内置域名规则
54-
├── background.js - Service Worker 入口(ESM;normalizeMessageError、应答信封)
55-
│ └── background/ - SW 模块(shared/rule-engine/lifecycle/message-handlers)
56-
├── bootstrap.purged.css - PurgeCSS 裁剪产物(263KB→52KB,CI 校验与源同步)
57-
└── bootstrap.min.css - Bootstrap 源文件(仅裁剪输入,不直接引用)
9+
├── manifest.json - MV3 清单(SW: src/background/background.js,popup: src/popup/popup.html)
10+
├── domain-rules.json - 内置域名规则(web_accessible_resource,保留根目录)
11+
├── bootstrap.min.css - Bootstrap 源文件(仅裁剪输入,不直接引用)
12+
├── bootstrap.purged.css - PurgeCSS 裁剪产物(页面实际引用)
13+
├── package.json / tsconfig.json / biome.json / renovate.json
14+
├── _locales/ - manifest 元数据国际化(chrome.i18n)
15+
├── docs/ - 文档(含 screenshots/)
16+
├── scripts/ - CI 校验/构建脚本
17+
├── tests/ - vitest 测试
18+
├── images/ fonts/ - 静态资源
19+
└── src/ - 全部运行时代码
20+
├── popup/ - 弹窗页
21+
│ ├── popup.html - 页面(单 module 入口 popup.js)
22+
│ ├── popup.js - 入口:消息接线、init、storage.onChanged
23+
│ ├── shared.js - getEl / runDOMUpdate
24+
│ ├── ui-sync.js - DOM 同步/显示辅助 + 快速检查(含防重入控制器)
25+
│ ├── language-apply.js - 语言应用流程
26+
│ └── update-check.js - 更新检查(含防抖)
27+
├── debug/ - 调试页
28+
│ ├── debug.html - 页面(单 module 入口 debug-ui.js)
29+
│ ├── debug-ui.js - 入口:init 编排
30+
│ ├── debug-headers.js - 控制台工具(window.debugHeaders)
31+
│ └── *.js - safe-dom/log-panel/rules-view/header-test/cache-mgmt/custom-language/rules-fix/diagnostics/auto-switch/domain-rules
32+
├── detect/ - 检测页
33+
│ ├── detect.html - 页面(单 module 入口 detect-ai.js,vendor classic 在前)
34+
│ ├── detect.js - 采集编排 + DetectPageContext(AI 侧唯一读接口)
35+
│ ├── detect-ai.js - 入口:sanitizeSnapshotForAI、init、事件监听
36+
│ ├── shared.js - translate/getUiLanguage/createMessageId
37+
│ ├── collectors.js - 全部 collect*(含 UA-CH 高熵值)
38+
│ ├── renderers.js - 全部 render*
39+
│ ├── snapshot.js - 快照状态 + buildDetectionSnapshot + CustomEvent 派发
40+
│ ├── ai-shared.js - AI 面板元素缓存 + aiSessionState + 状态助手
41+
│ ├── ai-config.js - AI 配置存储/校验/持久化
42+
│ ├── ai-ui.js - 聊天渲染与导出
43+
│ └── ai-session.js - 会话流程与 Prompt
44+
├── background/ - Service Worker
45+
│ ├── background.js - 入口:i18n 注册、normalizeMessageError、应答信封、listener 装配
46+
│ ├── shared.js - i18nReady、常量、sendBackgroundLog、notifyPopupUIUpdate、autoSwitch 状态
47+
│ ├── lifecycle.js - 初始化、上下文菜单、启动/安装/标签页监听
48+
│ ├── rule-engine.js - DNR 规则应用(互斥锁 + 重试)
49+
│ ├── message-handlers.js - MESSAGE_HANDLERS 分发表 + 全部处理器
50+
│ └── domain-rules-manager.js - 域名规则(LRU 缓存;customDomainRules 扩展点)
51+
├── shared/ - 跨页共享模块
52+
│ ├── message-types.js - 消息协议常量(MessageTypes,13 个)
53+
│ ├── storage-keys.js - 存储键常量(STORAGE_KEYS / LOCAL_STORAGE_KEYS)
54+
│ ├── shared-utils.js - 日志、语言检测、i18n 注册表、兜底翻译
55+
│ ├── shared-i18n-base.js - BaseI18n(字典静态注入 + data-i18n 扫描器)
56+
│ ├── shared-actions.js - requestBackground 信封 + resetAcceptLanguage
57+
│ ├── shared-update-checker.js - UpdateChecker(版本比较/缓存/https 校验)
58+
│ ├── shared-language-options.js / header-check-utils.js / shared-resource-manager.js
59+
│ ├── ai-provider-presets.js / ai-chat-client.js / copy-button.js(+css) / md5.js
60+
│ ├── theme-init.js - 主题初始化(classic script,预渲染同步执行)
61+
│ ├── toggle.js / toggle.css - 语言/主题切换组件(各页面 side-effect 导入)
62+
│ ├── page-base.css - 页面公共基础样式(字体定义,单一来源)
63+
│ └── vendor/ - marked / DOMPurify(classic script)
64+
└── i18n/ - 运行时 i18n(每组件单文件双语字典 + 实例模块)
65+
├── popup-dict.js / popup-i18n.js
66+
├── debug-dict.js / debug-i18n.js
67+
├── detect-dict.js / detect-i18n.js
68+
└── background-dict.js / background-i18n.js (Service Worker)
5869
```
5970

6071
## 架构契约
6172

62-
- **消息协议**:页面与 SW 之间的消息类型一律使用 `shared/message-types.js``MessageTypes`,禁止字符串字面量;响应信封 `{ ok, data } / { ok, error }`
63-
- **存储键**:一律使用 `shared/storage-keys.js` 常量。`currentLanguage` / `autoSwitchEnabled` 由 background 单写者持久化;`uiState` 位于 `chrome.storage.session`,页面经 `storage.onChanged` 只读订阅。
64-
- **i18n**:字典静态注入 `BaseI18n` 子类;运行时切换语言能力保留(`app-lang` localStorage + reload)。
73+
- **消息协议**:页面与 SW 之间的消息类型一律使用 `src/shared/message-types.js``MessageTypes`,禁止字符串字面量;响应信封 `{ ok, data } / { ok, error }`
74+
- **存储键**:一律使用 `src/shared/storage-keys.js` 常量。`currentLanguage` / `autoSwitchEnabled` 由 background 单写者持久化(自动切换未显式开启时默认关闭)`uiState` 位于 `chrome.storage.session`,页面经 `storage.onChanged` 只读订阅。
75+
- **i18n**:字典静态注入 `BaseI18n` 子类;运行时切换语言能力保留(`app-lang` localStorage + reload);i18n 实例经 `registerI18nInstance` 注册,禁止全局嗅探
6576
- **页面加载**:每页单个 `<script type="module">` 入口;`theme-init.js` 是唯一 classic 业务脚本(预渲染防主题闪烁,"theme" 键与 `LOCAL_STORAGE_KEYS.THEME` 保持同步)。
6677
- **类型**:JSDoc + `tsc --checkJs``npm run check:types`),CI 强制零错误。
6778
- **i18n DOM**:静态文本用 `data-i18n` / `data-i18n-title` / `data-i18n-placeholder` / `data-i18n-alt` 声明(禁止嵌套 data-i18n);`_applyDataAttributes` 统一扫描;特殊结构保留命令式。
6879
- **样式**:HTML 引用 `bootstrap.purged.css`(产物);改动任何 class 后跑 `npm run build:css`,CI 校验产物与源同步。
69-
- **检测页解耦**:detect.js 不引用 detect-ai.js;快照生命周期经 `detect:snapshot-updated` / `detect:run-finished` CustomEvent 传播。
80+
- **检测页解耦**:detect.js 不引用 detect-ai.js;AI 侧经 `DetectPageContext` 读快照;快照生命周期经 `detect:snapshot-updated` / `detect:run-finished` CustomEvent 传播。

manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"author": "ChuwuYo",
1010
"incognito": "spanning",
1111
"action": {
12-
"default_popup": "popup.html",
12+
"default_popup": "src/popup/popup.html",
1313
"default_icon": {
1414
"16": "images/icon16.png",
1515
"32": "images/icon32.png",
@@ -21,7 +21,7 @@
2121
}
2222
},
2323
"background": {
24-
"service_worker": "background.js",
24+
"service_worker": "src/background/background.js",
2525
"type": "module"
2626
},
2727
"permissions": ["storage", "declarativeNetRequest", "declarativeNetRequestFeedback", "tabs", "contextMenus"],

scripts/purge-css.mjs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,7 @@ import { PurgeCSS } from "purgecss";
33
// bootstrap 类可能经 JS 字符串/模板动态拼接,safelist 宁宽勿缺:
44
// 保证零视觉回归优先于极限体积
55
const result = await new PurgeCSS().purge({
6-
content: [
7-
"*.html",
8-
"background.js",
9-
"popup.js",
10-
"debug-ui.js",
11-
"debug-headers.js",
12-
"detect.js",
13-
"detect-ai.js",
14-
"toggle.js",
15-
"popup/**/*.js",
16-
"debug/**/*.js",
17-
"detect/**/*.js",
18-
"shared/**/*.js",
19-
"i18n/**/*.js",
20-
],
6+
content: ["src/**/*.html", "src/**/*.js"],
217
css: ["bootstrap.min.css"],
228
safelist: {
239
// 内容扫描已覆盖 HTML/JS 字面量;此处仅保底运行时经 JS setAttribute 注入的

background.js renamed to src/background/background.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// 后台脚本,确保扩展在浏览器启动时就能应用语言设置
22
// 入口仅保留统一响应辅助函数与模块装配,业务逻辑位于 background/ 目录下的各模块
33

4-
import { registerI18nInstance } from "./shared/shared-utils.js";
5-
import { backgroundI18n } from "./i18n/background-i18n.js";
6-
import { setupMessageListener } from "./background/message-handlers.js";
4+
import { registerI18nInstance } from "../shared/shared-utils.js";
5+
import { backgroundI18n } from "../i18n/background-i18n.js";
6+
import { setupMessageListener } from "./message-handlers.js";
77

88
registerI18nInstance("background", backgroundI18n);
99

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// 域名规则管理器
2-
import { STORAGE_KEYS } from "./shared/storage-keys.js";
2+
import { STORAGE_KEYS } from "../shared/storage-keys.js";
33

44
export class DomainRulesManager {
55
constructor() {
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { STORAGE_KEYS } from "../shared/storage-keys.js";
44
import { backgroundI18n } from "../i18n/background-i18n.js";
5-
import { domainRulesManager } from "../domain-rules-manager.js";
5+
import { domainRulesManager } from "./domain-rules-manager.js";
66
import { ResourceManager } from "../shared/shared-resource-manager.js";
77
import {
88
DEFAULT_LANG_EN,
@@ -130,9 +130,9 @@ const createContextMenusOnce = async () => {
130130

131131
chrome.contextMenus.onClicked.addListener((info, _tab) => {
132132
if (info.menuItemId === "open-detect-page") {
133-
chrome.tabs.create({ url: chrome.runtime.getURL("detect.html") });
133+
chrome.tabs.create({ url: chrome.runtime.getURL("src/detect/detect.html") });
134134
} else if (info.menuItemId === "open-debug-page") {
135-
chrome.tabs.create({ url: chrome.runtime.getURL("debug.html") });
135+
chrome.tabs.create({ url: chrome.runtime.getURL("src/debug/debug.html") });
136136
}
137137
});
138138

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { MessageTypes } from "../shared/message-types.js";
44
import { STORAGE_KEYS } from "../shared/storage-keys.js";
55
import { backgroundI18n } from "../i18n/background-i18n.js";
6-
import { domainRulesManager } from "../domain-rules-manager.js";
6+
import { domainRulesManager } from "./domain-rules-manager.js";
77
import {
88
DEFAULT_LANG_EN,
99
RULE_ID,

0 commit comments

Comments
 (0)