diff --git a/src/content/components/config-panel.ts b/src/content/components/config-panel.ts
index e2b189d..7d921ee 100644
--- a/src/content/components/config-panel.ts
+++ b/src/content/components/config-panel.ts
@@ -208,7 +208,7 @@ export class ConfigPanel extends LitElement {
${!showExecutionView && hasConflicts
? html`
- ⚠️ 检测到 ${this.conflictCount} 个冲突
+ ⚠️ ${I18nService.t('conflicts_detected', [String(this.conflictCount)])}
`
: ''}
diff --git a/src/content/components/file-selector-panel.ts b/src/content/components/file-selector-panel.ts
index 9e87a5a..1a95472 100644
--- a/src/content/components/file-selector-panel.ts
+++ b/src/content/components/file-selector-panel.ts
@@ -446,7 +446,7 @@ export class FileSelectorPanel extends LitElement {
// Confirm if there are conflicts
if (this.conflictIds.size > 0) {
- const conflictMessage = `检测到 ${this.conflictIds.size} 个冲突,是否继续?`;
+ const conflictMessage = I18nService.t('conflicts_detected_confirm', [String(this.conflictIds.size)]);
const confirmed = confirm(conflictMessage);
if (!confirmed) {
diff --git a/src/content/components/preview-panel.ts b/src/content/components/preview-panel.ts
index 863e750..e77b07b 100644
--- a/src/content/components/preview-panel.ts
+++ b/src/content/components/preview-panel.ts
@@ -60,9 +60,7 @@ export class PreviewPanel extends LitElement {
-
- 检测到 ${this.conflictCount} 个冲突
-
+ ${I18nService.t('conflicts_detected', [String(this.conflictCount)])}
`
: ''}
@@ -70,7 +68,7 @@ export class PreviewPanel extends LitElement {
- 预览项:
+ ${I18nService.t('preview_items')}
${this.items.length}
${this.showStatus
@@ -92,7 +90,7 @@ export class PreviewPanel extends LitElement {
${hasConflicts
? html`
- 冲突:
+ ${I18nService.t('preview_summary_conflict')}
${this.conflictCount}
`
@@ -104,7 +102,7 @@ export class PreviewPanel extends LitElement {
? html`
-
正在生成预览...
+
${I18nService.t('preview_loading')}
`
: html`
diff --git a/src/content/components/toolbar.ts b/src/content/components/toolbar.ts
index 905a23d..cd1ca79 100644
--- a/src/content/components/toolbar.ts
+++ b/src/content/components/toolbar.ts
@@ -152,7 +152,7 @@ export class Toolbar extends LitElement {
- 已选择 ${this.selectedCount} / ${this.totalCount} 个文件
+ ${I18nService.t('selected_files_count', [String(this.selectedCount), String(this.totalCount)])}
diff --git a/src/content/components/virtual-file-list.ts b/src/content/components/virtual-file-list.ts
index 3fc77b9..1055274 100644
--- a/src/content/components/virtual-file-list.ts
+++ b/src/content/components/virtual-file-list.ts
@@ -2,6 +2,7 @@ import { LitElement, html, css, type PropertyValues } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { virtualize, virtualizerRef } from '@lit-labs/virtualizer/virtualize.js';
import { FileItem } from '../../types/platform';
+import { I18nService } from '../../utils/i18n';
/**
* Virtual File List Component
@@ -201,7 +202,7 @@ export class VirtualFileList extends LitElement {
- 没有找到文件
+ ${I18nService.t('no_files_found')}
`;
}
diff --git a/src/content/components/virtual-preview-list.ts b/src/content/components/virtual-preview-list.ts
index 2065038..5c88ec9 100644
--- a/src/content/components/virtual-preview-list.ts
+++ b/src/content/components/virtual-preview-list.ts
@@ -153,7 +153,7 @@ export class VirtualPreviewList extends LitElement {
- 选择文件并配置规则以查看预览
+ ${I18nService.t('preview_empty_hint')}
`;
}
diff --git a/src/content/index.ts b/src/content/index.ts
index 36d99ef..07ee4fa 100644
--- a/src/content/index.ts
+++ b/src/content/index.ts
@@ -169,36 +169,60 @@ function cleanupOldInstances(): void {
* @param platform - 平台名称 (aliyun/baidu/quark)
*/
async function injectPageScriptToMainWorld(platform: 'aliyun' | 'baidu' | 'quark'): Promise {
+ // ✅ 引用编译后的 .js 文件(Vite 会将 TypeScript 编译为 JavaScript)
+ const scriptPath = `src/adapters/${platform}/page-script.js`;
+ const scriptURL = chrome.runtime.getURL(scriptPath);
+ let script: HTMLScriptElement | null = null;
+
try {
- // ✅ 引用编译后的 .js 文件(Vite 会将 TypeScript 编译为 JavaScript)
- const scriptPath = `src/adapters/${platform}/page-script.js`;
- const scriptURL = chrome.runtime.getURL(scriptPath);
-
// 创建 script 标签并注入到页面 (MAIN world)
- const script = document.createElement('script');
- script.src = scriptURL;
- script.type = 'module'; // 支持 ES6 模块
+ const scriptEl = document.createElement('script');
+ script = scriptEl;
+ scriptEl.src = scriptURL;
+ scriptEl.type = 'module'; // 支持 ES6 模块
- // 等待脚本加载完成
+ // 等待脚本加载完成(加超时保护,避免 CSP/站点脚本拦截导致 onload/onerror 永不触发,从而阻塞整个悬浮按钮注入)
+ const timeoutMs = 1500;
await new Promise((resolve, reject) => {
- script.onload = () => {
+ const cleanup = () => {
+ scriptEl.onload = null;
+ scriptEl.onerror = null;
+ };
+
+ const timer = window.setTimeout(() => {
+ cleanup();
+ reject(new Error(`Timeout loading page-script for ${platform}`));
+ }, timeoutMs);
+
+ scriptEl.onload = () => {
+ window.clearTimeout(timer);
+ cleanup();
resolve();
};
- script.onerror = () => {
+ scriptEl.onerror = () => {
+ window.clearTimeout(timer);
+ cleanup();
reject(new Error(`Failed to load page-script for ${platform}`));
};
// 注入到页面
- (document.head || document.documentElement).appendChild(script);
+ (document.head || document.documentElement).appendChild(scriptEl);
});
// 脚本加载后可以移除标签 (代码已执行)
- script.remove();
+ scriptEl.remove();
logger.info(`${platform} page-script injected to MAIN world successfully`);
} catch (error) {
- logger.error(`Failed to inject ${platform} page-script:`, error instanceof Error ? error : new Error(String(error)));
- throw error;
+ // 不要阻塞悬浮按钮注入:即使 page-script 注入失败,也应该让 UI 先显示出来
+ // 相关平台的 API 调用可能会退化/失败,但用户至少能看到按钮和面板,且能在控制台看到诊断信息
+ logger.warn(
+ `[DIAG] Failed to inject ${platform} page-script (continue without it):`,
+ error instanceof Error ? error : new Error(String(error))
+ );
+ } finally {
+ // Best-effort cleanup: 避免遗留无用的