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:
+125
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* JWT 解析工具
|
||||
*/
|
||||
|
||||
export interface JwtHeader {
|
||||
alg: string;
|
||||
typ?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface JwtPayload {
|
||||
iss?: string;
|
||||
sub?: string;
|
||||
aud?: string | string[];
|
||||
exp?: number;
|
||||
nbf?: number;
|
||||
iat?: number;
|
||||
jti?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface JwtResult {
|
||||
header: JwtHeader | null;
|
||||
payload: JwtPayload | null;
|
||||
signature: string;
|
||||
raw: {
|
||||
header: string;
|
||||
payload: string;
|
||||
signature: string;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64URL 解码
|
||||
* @param str Base64URL 编码字符串
|
||||
*/
|
||||
export function decodeBase64Url(str: string): string {
|
||||
// 将 Base64URL 转换为 标准 Base64
|
||||
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
|
||||
// 添加填充
|
||||
const pad = base64.length % 4;
|
||||
if (pad) {
|
||||
if (pad === 1) {
|
||||
throw new Error('Invalid base64url string');
|
||||
}
|
||||
base64 += new Array(5 - pad).join('=');
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 TextDecoder 处理 UTF-8 字符
|
||||
const binStr = atob(base64);
|
||||
const binLen = binStr.length;
|
||||
const bytes = new Uint8Array(binLen);
|
||||
for (let i = 0; i < binLen; i++) {
|
||||
bytes[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
return decoder.decode(bytes);
|
||||
} catch (e) {
|
||||
throw new Error('Failed to decode base64url: ' + (e instanceof Error ? e.message : String(e)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JWT 字符串
|
||||
* @param token JWT 字符串
|
||||
*/
|
||||
export function parseJwt(token: string): JwtResult {
|
||||
const parts = token.trim().split('.');
|
||||
|
||||
if (parts.length !== 3) {
|
||||
return {
|
||||
header: null,
|
||||
payload: null,
|
||||
signature: '',
|
||||
raw: { header: '', payload: '', signature: '' },
|
||||
error: 'JWT 格式错误:必须包含三个由 "." 分隔的部分',
|
||||
};
|
||||
}
|
||||
|
||||
const [headerB64, payloadB64, signatureB64] = parts;
|
||||
const result: JwtResult = {
|
||||
header: null,
|
||||
payload: null,
|
||||
signature: signatureB64,
|
||||
raw: {
|
||||
header: headerB64,
|
||||
payload: payloadB64,
|
||||
signature: signatureB64,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const headerJson = decodeBase64Url(headerB64);
|
||||
result.header = JSON.parse(headerJson);
|
||||
} catch (e) {
|
||||
result.error = '解析 Header 失败:' + (e instanceof Error ? e.message : String(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
const payloadJson = decodeBase64Url(payloadB64);
|
||||
result.payload = JSON.parse(payloadJson);
|
||||
} catch (e) {
|
||||
result.error = '解析 Payload 失败:' + (e instanceof Error ? e.message : String(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 JSON
|
||||
* @param obj 对象
|
||||
*/
|
||||
export function formatJson(obj: unknown): string {
|
||||
try {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
} catch (e) {
|
||||
console.error('格式化 JSON 失败:', e);
|
||||
return String(obj);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user