refactor(dev): 前端模块化 Step 2 收官 — 抽出 chat.js,main 缩成 75 行入口
最后也最缠的一块:任务列表(浏览/筛选/滚动)+ selectTask 切换 + renderChatMeta/模型下拉 + renderMessages + live-run 助手 + sendMessage/cancel + fetchSse/handleSseEvent + 润色/粘贴文件 + 完成/废弃/删除/导出/清空 (原 main 连续区 64–1132)→ chat.js(1086 行)。 决策:合一个 chat.js 而非强拆 tasks.js+stream.js —— 二者共享 state.liveRuns + chat-stream DOM + run 生命周期,live-run 助手被 selectTask 与 SSE 机器两边调用、骑墙;强拆会制造双向各 ~4-5 个 import 且边界不自然。 - 导出 loadTaskList / loadModels / selectTask;embed/files/newtask 对这 三个的 import 从 ./main.js 改指 ./chat.js;formatUploadProgress 加 export。 - chat 不调 enterApp → 与 main 无环。 - main.js 仅留 enterApp(编排)+ loadStorage + Esc 关栈 + boot = 75 行入口, import 精简到 11 行(layout/markdown/media 不再被 main 直接引用,但经 chat 仍在依赖图、副作用照常)。 校验升级:node 全检 + import/export 一致性 + 从 main BFS 的模块可达性 (14/14 可达,确保副作用模块不掉出图)。 dev.html 4087 行单文件 → 14 个零构建 ES module + 纯 HTML;main 2719→75。 路径 1(拆文件)完成。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
36dbdb2dda
commit
15ecf45c93
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||
|
||||
最后更新:2026-06-07(前端模块化 Step 2:抽出 … / newtask / embed.js;main 1154 行)
|
||||
最后更新:2026-06-08(前端模块化 Step 2 完成:抽出 chat.js;main 75 行,路径 1 收官)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
### 2026-06-06
|
||||
|
||||
- **前端模块化 Step 2 收官:抽出 `chat.js`(对话视图)+ main.js 缩成 75 行入口**:最后也是最缠的一块——任务列表(浏览/筛选/滚动)+ selectTask 切换 + renderChatMeta/模型下拉 + renderMessages + live-run 助手 + sendMessage/cancel + fetchSse/handleSseEvent + 润色/粘贴文件 + 完成/废弃/删除/导出/清空(原 main.js 连续区 64–1132)→ `chat.js`(1086 行)。**决策:合一个 chat.js 而非强拆 tasks.js+stream.js**——读完依赖图确认二者共享 `state.liveRuns` + `chat-stream` DOM + run 生命周期,且 live-run 助手(renderLiveRunIfVisible/ensureRunningTaskSubscribed 等)被 selectTask 和 SSE 机器两边调用、骑墙;强拆会制造双向各 ~4-5 个 import 且边界不自然(用户已确认选合一)。导出 `loadTaskList`/`loadModels`/`selectTask`,embed/files/newtask 对这三个的 import 从 `./main.js` 改指 `./chat.js`;`formatUploadProgress` 加 export(粘贴上传进度用)。**chat 不调 enterApp → 与 main 无环**。`main.js` 仅留 `enterApp`(编排)+ `loadStorage` + Esc 关栈 + boot = **75 行入口**,import 精简到 11 行(layout/markdown/media 不再被 main 直接引用,但经 chat 仍在依赖图、副作用照常)。**校验升级**:除 node 全检 + import/export 一致性,新增**从 main BFS 的模块可达性检查**(14/14 可达,确保副作用模块不掉出图)。dev.html 4087 行单文件 → 14 个零构建 ES module + 纯 HTML;main 2719→75。**路径 1(拆文件)完成**,后续可按需进路径 2(给 chat/files 等局部引 Alpine/petite-vue 响应式)。
|
||||
- **前端模块化 Step 2:抽出 `embed.js`(iframe 模式)**:父页面经 postMessage 推 token 进入应用 + 401 重签(原 main.js 1147–1209 + 顶层 `_embedInitialTaskHandled` 一次性标志)→ `embed.js`(75 行)。导出 `embedInit`(boot 调)+ `embedPostToParent`/`embedShowWaiting`(auth 的 logout 在 embed 下通知父页面/显示等待态)——后两个从 main 迁出后,`auth.js` 对它们的 import 从 `./main.js` 改指 `./embed.js`(auth 仍从 main import enterApp)。反向 import main glue `enterApp`/`loadTaskList`/`selectTask`。main↔embed、auth↔embed 均运行时调用环,安全。main.js 删至 **1154 行**(2719 行起,已搬出约 58%)。node 全检过、import/export 一致性过、静态测试 2 过。剩 main 内:`enterApp` glue + tasks(列表/选择/渲染消息)+ stream(发送/SSE)+ boot + Esc 关栈,待最后一并处理 tasks+stream。
|
||||
- **前端模块化 Step 2:抽出 `newtask.js`(新建任务弹框)**:任务名 / 工作目录(新建 sentinel 或复用已有 + 二级 input 联动)/ 描述 / skill / 模型 select,提交 `POST /v1/tasks`(原 main.js 1146–1320)→ `newtask.js`(186 行)。顶层自绑 hd-new 打开 / nt-go 提交 / 各 input 联动;唯一对外导出 `loadFolderSuggestions`(供 main enterApp 初始化顶部 filter-wd、files 复制/移动后刷目录)——它从 main 迁来后,`files.js` 对它的 import 从 `./main.js` 改指 `./newtask.js`。反向 import main glue `loadModels`(加 `export`)/`loadTaskList`/`selectTask` + `logout`(auth)。main.js 删至 1220 行。node 全检过、import/export 一致性校验过、私有符号清零。
|
||||
- **前端模块化 Step 2:抽出 `media.js`(工具活动标签 + artifact 抽取/渲染)+ 收敛 downloadFile 反向依赖**:对话内 `toolActivityLabel`(工具调用→中文活动名)、`extractArtifactRels`(从结果文本/working_dir 提产物路径)、`extractMediaBanner`(seedream/seedance 横幅)、`renderArtifactBarHtml`(产物 chip 条 + 图/视频内联占位)、`upgradeMediaArtifacts`(占位异步 fetch blob 填 `<img>`/`<video>` 带缓存)、`downloadFile`(blob 下载)→ `media.js`(237 行,原 main.js 1134–1359)。**收敛点**:downloadFile 移入 media 后,`preview.js`/`files.js` 对它的 import 从 `./main.js` 改指 `./media.js` —— 把这条反向依赖从 main 挪开。media 导入极少(`escapeHtml`/`_categorize`(preview)/`state`/`logout`),与 preview 成 media↔preview 环(均运行时调用,安全)。**两次险漏靠校验抓回**:① 共享 const `ARTIFACT_PRODUCING_TOOLS`(main renderMessages/SSE 用 4 处,`.has()` 访问非函数调用,"被调标识符"法漏掉)② 内部函数 `_flushMediaArtifactCache`(selectTask 切任务清缓存用)—— 残留符号检查发现后补 export。新增**全模块 import/export 一致性校验脚本**(每个 `import{X}` 必在目标 `export`),11 模块全过。main.js 删至 1393 行。`node --check` 11 模块全过、静态测试 2 过。
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,8 @@
|
|||
// embedPostToParent / embedShowWaiting(auth 的 logout 在 embed 下通知父页面/显示等待态)。
|
||||
import { state, LS_TOKEN, LS_UID, LS_NAME, EMBED_PARENT_ORIGIN, EMBED_INITIAL_TASK_ID } from "./state.js";
|
||||
import { $ } from "./dom.js";
|
||||
import { enterApp, loadTaskList, selectTask } from "./main.js";
|
||||
import { enterApp } from "./main.js";
|
||||
import { loadTaskList, selectTask } from "./chat.js";
|
||||
|
||||
// embed 首个 task 自动定位的一次性标志(仅 embed 段使用)
|
||||
let _embedInitialTaskHandled = false;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { escapeHtml, humanSize } from "./format.js";
|
|||
import { openFilePreview } from "./preview.js";
|
||||
import { logout } from "./auth.js";
|
||||
import { downloadFile } from "./media.js";
|
||||
import { selectTask, loadTaskList } from "./main.js";
|
||||
import { selectTask, loadTaskList } from "./chat.js";
|
||||
import { loadFolderSuggestions } from "./newtask.js";
|
||||
|
||||
// ───── files(user-rooted,不绑 task) ─────
|
||||
|
|
@ -349,7 +349,7 @@ function uploadFilesLabel(files) {
|
|||
if (!files || !files.length) return "";
|
||||
return files.length === 1 ? files[0].name : `${files[0].name} 等 ${files.length} 个文件`;
|
||||
}
|
||||
function formatUploadProgress(files, loaded, total) {
|
||||
export function formatUploadProgress(files, loaded, total) {
|
||||
const denom = total || uploadTotalBytes(files);
|
||||
const pct = denom ? Math.min(100, Math.max(0, Math.round((loaded / denom) * 100))) : 0;
|
||||
const sizeText = denom ? ` · ${humanSize(Math.min(loaded, denom))}/${humanSize(denom)}` : "";
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,7 +6,7 @@ import { state } from "./state.js";
|
|||
import { api } from "./api.js";
|
||||
import { escapeHtml } from "./format.js";
|
||||
import { logout } from "./auth.js";
|
||||
import { loadModels, loadTaskList, selectTask } from "./main.js";
|
||||
import { loadModels, loadTaskList, selectTask } from "./chat.js";
|
||||
|
||||
// ───── new task ─────
|
||||
// wd 二级 input 默认跟随 name;用户一旦手改二级 input → 脱钩;清空二级 input 重置 flag
|
||||
|
|
|
|||
Loading…
Reference in New Issue