refactor: 模块化架构重构
将单文件巨型 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行纯模板
This commit is contained in:
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* State - 轻量级响应式状态管理
|
||||
* 使用发布-订阅模式,状态变更时通知所有订阅者
|
||||
*/
|
||||
|
||||
class AppState {
|
||||
constructor() {
|
||||
this._state = {};
|
||||
this._listeners = new Map(); // key → Set<callback>
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态值并通知订阅者
|
||||
* @param {string} key - 状态键名
|
||||
* @param {*} value - 状态值
|
||||
*/
|
||||
set(key, value) {
|
||||
const old = this._state[key];
|
||||
this._state[key] = value;
|
||||
if (old !== value && this._listeners.has(key)) {
|
||||
for (const cb of this._listeners.get(key)) {
|
||||
try { cb(value, old); } catch (e) { console.error(`[State] 监听器错误 (${key}):`, e); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态值
|
||||
* @param {string} key - 状态键名
|
||||
* @param {*} [defaultValue] - 默认值
|
||||
* @returns {*}
|
||||
*/
|
||||
get(key, defaultValue = undefined) {
|
||||
return key in this._state ? this._state[key] : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅状态变更
|
||||
* @param {string} key - 状态键名
|
||||
* @param {function} callback - 回调函数 (newValue, oldValue) => void
|
||||
* @returns {function} 取消订阅函数
|
||||
*/
|
||||
on(key, callback) {
|
||||
if (!this._listeners.has(key)) {
|
||||
this._listeners.set(key, new Set());
|
||||
}
|
||||
this._listeners.get(key).add(callback);
|
||||
return () => this._listeners.get(key)?.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置初始状态
|
||||
* @param {Object} initial - 初始状态对象
|
||||
*/
|
||||
init(initial) {
|
||||
Object.assign(this._state, initial);
|
||||
}
|
||||
}
|
||||
|
||||
// 单例
|
||||
export const state = new AppState();
|
||||
|
||||
// ── 状态键名常量 ──
|
||||
export const KEYS = {
|
||||
DB: 'db',
|
||||
API: 'api',
|
||||
CURRENT_SESSION: 'currentSession',
|
||||
IS_STREAMING: 'isStreaming',
|
||||
IS_HISTORY_VIEW: 'isHistoryView',
|
||||
PENDING_IMAGES: 'pendingImages',
|
||||
ABORT_CONTROLLER: 'abortController',
|
||||
SYSTEM_PROMPT: 'systemPrompt',
|
||||
SYSTEM_PROMPT_ENABLED: 'systemPromptEnabled',
|
||||
NUM_CTX: 'numCtx',
|
||||
HISTORY_PAGE: 'historyPage',
|
||||
HISTORY_SEARCH: 'historySearchQuery',
|
||||
};
|
||||
Reference in New Issue
Block a user