Enhance form recognition, optimize UI, and unify components (#19)
- **docs**: 完善组件注释、README 目录结构及 AGENTS.md 文档。 - **refactor**: - 提取通用 `PageHeader`、`Button`、`DashboardCard` 及 `ErrorBoundary` 组件。 - 重构消息通信机制,采用 `@webext-core/messaging` 实现类型安全。 - 将全局通知系统重构为 `SnackbarProvider` (后合并至 `GlobalSnackbar`)。 - 迁移样式系统至 MUI 主题,移除冗余 CSS。 - 优化路由配置,支持独立标签页模式及页面懒加载。 - 移除未使用文件、URL 工具及表单映射相关功能。 - **feat**: - 新增配置导出功能(JSON)及状态提示。 - 新增侧边栏状态变化通知机制。 - 新增文本统计及 JWT 解析工具。 - 优化二维码生成与解析逻辑,换用更轻量的 `qrious` 和 `qr-scanner`。 - 增强高亮器功能,支持闪烁效果及 Shadow DOM 穿透。 - **style**: 优化仪表盘响应式网格布局及 UI 细节。 - **fix**: 修复 `useStorageState` 依赖缺失及路由初始化性能问题。 - **test**: 更新单元测试以覆盖新增的工具函数及功能特性。
This commit is contained in:
+27
-121
@@ -1,154 +1,60 @@
|
||||
import { FormFieldInfo, FillMode } from './dummyDataGenerator';
|
||||
import { defineExtensionMessaging } from '@webext-core/messaging';
|
||||
|
||||
/**
|
||||
* 字段数据接口(用于消息传递)
|
||||
*/
|
||||
export interface MessageFieldData extends Omit<FormFieldInfo, 'element'> {
|
||||
useInvalidData?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息动作类型
|
||||
*/
|
||||
export enum MessageAction {
|
||||
// 标签页操作
|
||||
RELOAD_TAB = 'reloadTab',
|
||||
|
||||
// 表单相关操作
|
||||
SCAN_FORM_FIELDS = 'scanFormFields',
|
||||
FILL_VALID_DATA = 'fillValidData',
|
||||
FILL_INVALID_DATA = 'fillInvalidData',
|
||||
FILL_SELECTED_FIELDS = 'fillSelectedFields',
|
||||
CLEAR_ALL_FIELDS = 'clearAllFields',
|
||||
|
||||
// 字段高亮操作
|
||||
HIGHLIGHT_FIELD = 'highlightField',
|
||||
UNHIGHLIGHT_FIELD = 'unhighlightField',
|
||||
HIGHLIGHT_ALL_FIELDS = 'highlightAllFields',
|
||||
UNHIGHLIGHT_ALL_FIELDS = 'unhighlightAllFields',
|
||||
|
||||
// 字段定位/闪烁
|
||||
FLASH_FIELD = 'flashField',
|
||||
|
||||
// 智能表单注入
|
||||
FORM_INJECT = 'FORM_INJECT',
|
||||
|
||||
// 侧边栏状态变化
|
||||
SIDE_PANEL_STATE_CHANGED = 'sidePanelStateChanged',
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息载荷接口 (保留兼容性)
|
||||
*/
|
||||
export interface MessagePayload {
|
||||
action: MessageAction | string;
|
||||
tabId?: number;
|
||||
delay?: number;
|
||||
fields?: MessageFieldData[];
|
||||
mode?: FillMode;
|
||||
includeHidden?: boolean;
|
||||
fieldId?: string;
|
||||
fieldIds?: string[];
|
||||
data?: unknown;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息响应接口
|
||||
*/
|
||||
export interface FormInjectItem {
|
||||
entry: import('@/types/storage').FormMapEntry;
|
||||
mockValue: string;
|
||||
}
|
||||
|
||||
export interface FormInjectResult {
|
||||
id: string;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface MessageResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
fields?: Omit<FormFieldInfo, 'element'>[];
|
||||
totalCount?: number;
|
||||
validCount?: number;
|
||||
hasModal?: boolean;
|
||||
results?: FormInjectResult[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 协议映射定义(用于类型安全的消息通信)
|
||||
*/
|
||||
export interface ProtocolMap {
|
||||
// 基础消息格式,用于逐步迁移
|
||||
[MessageAction.RELOAD_TAB](data: { tabId: number; delay?: number }): MessageResponse;
|
||||
[MessageAction.SCAN_FORM_FIELDS](): MessageResponse;
|
||||
[MessageAction.FILL_VALID_DATA](data: { includeHidden?: boolean }): MessageResponse;
|
||||
[MessageAction.FILL_INVALID_DATA](data: { includeHidden?: boolean }): MessageResponse;
|
||||
[MessageAction.FILL_SELECTED_FIELDS](data: {
|
||||
fields: MessageFieldData[];
|
||||
mode?: FillMode;
|
||||
includeHidden?: boolean;
|
||||
}): MessageResponse;
|
||||
[MessageAction.CLEAR_ALL_FIELDS](): MessageResponse;
|
||||
[MessageAction.HIGHLIGHT_FIELD](data: { fieldId: string }): MessageResponse;
|
||||
[MessageAction.UNHIGHLIGHT_FIELD](data: { fieldId: string }): MessageResponse;
|
||||
[MessageAction.HIGHLIGHT_ALL_FIELDS](data: { fieldIds: string[] }): MessageResponse;
|
||||
[MessageAction.UNHIGHLIGHT_ALL_FIELDS](): MessageResponse;
|
||||
[MessageAction.FLASH_FIELD](data: { fieldId: string }): MessageResponse;
|
||||
[MessageAction.FORM_INJECT](data: { data: FormInjectItem[] }): MessageResponse;
|
||||
[MessageAction.SIDE_PANEL_STATE_CHANGED](data: { isOpen: boolean }): void;
|
||||
}
|
||||
|
||||
export const { sendMessage, onMessage } = defineExtensionMessaging<ProtocolMap>();
|
||||
|
||||
/**
|
||||
* 发送消息到内容脚本 (旧版包装器,内部使用新机制)
|
||||
* @deprecated 建议直接使用 sendMessage
|
||||
*/
|
||||
type ProtocolData<K extends keyof ProtocolMap> = Parameters<ProtocolMap[K]>[0];
|
||||
type ProtocolReturn<K extends keyof ProtocolMap> = ReturnType<ProtocolMap[K]>;
|
||||
|
||||
export async function sendMessageToContent<K extends keyof ProtocolMap>(
|
||||
action: K,
|
||||
...args: ProtocolData<K> extends undefined ? [] : [data: ProtocolData<K>]
|
||||
): Promise<ProtocolReturn<K>> {
|
||||
...args: Parameters<ProtocolMap[K]>[0] extends undefined
|
||||
? []
|
||||
: [data: Parameters<ProtocolMap[K]>[0]]
|
||||
): Promise<ReturnType<ProtocolMap[K]>> {
|
||||
try {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab?.id) {
|
||||
return { success: false, message: '无法获取当前标签页' } as ProtocolReturn<K>;
|
||||
console.warn(`[Messaging] 无法获取当前标签页,无法发送动作: ${action}`);
|
||||
return { success: false, message: '无法获取当前标签页' } as ReturnType<ProtocolMap[K]>;
|
||||
}
|
||||
|
||||
const data = args.length > 0 ? args[0] : undefined;
|
||||
return await (
|
||||
sendMessage as (type: K, data: ProtocolData<K>, arg?: number) => Promise<ProtocolReturn<K>>
|
||||
)(action, data as ProtocolData<K>, tab.id);
|
||||
} catch (error) {
|
||||
console.error('发送消息失败:', error);
|
||||
return { success: false, message: '发送消息失败' } as ProtocolReturn<K>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入内容脚本
|
||||
*/
|
||||
export async function injectContentScript(): Promise<boolean> {
|
||||
try {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab?.id) {
|
||||
return false;
|
||||
const response = await (
|
||||
sendMessage as (
|
||||
type: K,
|
||||
data: Parameters<ProtocolMap[K]>[0],
|
||||
arg?: number,
|
||||
) => Promise<ReturnType<ProtocolMap[K]>>
|
||||
)(action, data as Parameters<ProtocolMap[K]>[0], tab.id);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error(`[Messaging] 向内容脚本发送消息失败 [Action: ${action}]:`, errorMsg);
|
||||
|
||||
if (errorMsg.includes('Could not establish connection')) {
|
||||
return { success: false, message: '无法连接到网页,请刷新页面后再试' } as ReturnType<
|
||||
ProtocolMap[K]
|
||||
>;
|
||||
}
|
||||
if (errorMsg.includes('No response')) {
|
||||
return { success: false, message: '网页响应超时,请重试' } as ReturnType<ProtocolMap[K]>;
|
||||
}
|
||||
|
||||
// 尝试注入内容脚本
|
||||
await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
files: ['/content-scripts/content.js'],
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('注入内容脚本失败:', error);
|
||||
return false;
|
||||
return { success: false, message: `通信失败: ${errorMsg}` } as ReturnType<ProtocolMap[K]>;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user