将单文件巨型 IIFE (1608行) 拆分为 14 个 ES Module: 核心层: - js/state.js - 轻量级响应式状态管理 (发布-订阅) - js/utils.js - 通用工具函数 - js/sanitizer.js - HTML XSS 净化器 - js/marked-config.js - Markdown 渲染器配置 组件层: - js/components/chat-area.js - 消息渲染/流式更新/导出 - js/components/input-area.js - 文本输入/图片上传/发送 - js/components/history-modal.js - 历史记录面板/搜索/分页 - js/components/settings-modal.js - 设置面板/数据管理 - js/components/header.js - 顶部导航/连接状态 - js/components/model-bar.js - 模型选择栏 - js/components/toast.js - Toast 通知 - js/components/lightbox.js - 图片预览 入口层: - js/app.js - 主入口/初始化协调 index.html: 1608行 → 204行纯模板
39 lines
1.2 KiB
JavaScript
39 lines
1.2 KiB
JavaScript
/**
|
|
* Markdown 渲染器配置
|
|
* 封装 marked.js,配置自定义渲染器和安全策略
|
|
*/
|
|
|
|
import { marked } from '../marked.esm.js';
|
|
import { SAFE_URI_SCHEMES } from './sanitizer.js';
|
|
|
|
// 配置 marked
|
|
marked.setOptions({
|
|
breaks: true, // 换行转 <br>
|
|
gfm: true, // GitHub 风格 Markdown
|
|
});
|
|
|
|
// 自定义渲染器:链接新窗口打开 + 安全属性
|
|
const renderer = new marked.Renderer();
|
|
|
|
renderer.link = function ({ href, title, text }) {
|
|
const safe = SAFE_URI_SCHEMES.has(new URL(href, 'https://placeholder').protocol);
|
|
const t = title ? ` title="${title}"` : '';
|
|
if (!safe) {
|
|
return `<span class="blocked-link" title="已阻止不安全链接">${text}</span>`;
|
|
}
|
|
return `<a href="${href}"${t} target="_blank" rel="noopener noreferrer">${text}</a>`;
|
|
};
|
|
|
|
renderer.image = function ({ href, title, text }) {
|
|
const safe = SAFE_URI_SCHEMES.has(new URL(href, 'https://placeholder').protocol);
|
|
if (!safe && !/^data:image\//i.test(href)) {
|
|
return `<span class="blocked-link">[已阻止不安全图片: ${text}]</span>`;
|
|
}
|
|
const t = title ? ` title="${title}"` : '';
|
|
return `<img src="${href}" alt="${text}"${t} loading="lazy">`;
|
|
};
|
|
|
|
marked.use({ renderer });
|
|
|
|
export { marked };
|