// 全局状态 + 持久化键 + embed 标志。 // (从 dev.html 内联脚本抽出;路径 1 模块化第一步,逻辑零改动) export const LS_TOKEN = "zcbot.token"; export const LS_UID = "zcbot.user_id"; export const LS_NAME = "zcbot.name"; export const LS_LEFT_COLLAPSED = "zcbot.left-collapsed"; export const LS_RIGHT_COLLAPSED = "zcbot.right-collapsed"; export const LS_LEFT_WIDTH = "zcbot.left-width"; export const LS_RIGHT_WIDTH = "zcbot.right-width"; export const LS_TASK_FILTERS_COLLAPSED = "zcbot.task-filters-collapsed"; // 左栏筛选区折叠偏好(默认折叠) // ?embed=1&parent_origin=https://... → iframe 模式;父页面用 postMessage 推 token // 可选 task_id=:首次签发 token 后自动定位到该 task 并加载消息 const _embedQS = new URLSearchParams(location.search); export const EMBED = _embedQS.get("embed") === "1"; export const EMBED_PARENT_ORIGIN = (_embedQS.get("parent_origin") || "").trim(); export const EMBED_INITIAL_TASK_ID = (_embedQS.get("task_id") || "").trim(); export const state = { token: localStorage.getItem(LS_TOKEN) || "", userId: localStorage.getItem(LS_UID) || "", userName: localStorage.getItem(LS_NAME) || "", taskId: null, taskMeta: null, filesPath: "", // 同 wd 内除自己外其他活跃 task(run_status in running/cancelling),供 banner 显示 concurrentWarnings: [], evtSrc: null, streaming: false, // 兼容旧判断:任一 task 是否在流式中 liveRuns: new Map(), // task_id -> 当前浏览器会话内运行中的回复卡/累计文本 taskProgressByTask: new Map(), // task_id -> 历史消息重放后的当前进度步骤 // 消息分页(尾部窗口 + 向上滚动加载更早):切 task 默认只拉最近一批, // 顶部 sentinel 进视口自动往前补。loadedMessages 是当前已加载的升序窗口, // renderMessages 对它做纯函数渲染(时序累积逻辑无需改)。 loadedMessages: [], msgHasMore: false, // 更早是否还有(后端 has_more) msgLoadingEarlier: false, // 向上加载在途标记,防 observer 重复触发 msgHasMoreNewer: false, // 更新是否还有(从目录跳到旧消息后,下方还有未加载的新消息) msgLoadingNewer: false, // 向下加载在途标记 // 消息目录(右侧悬浮圆点轨道):全部 user 轮次的 {idx, snippet},点圆点滚动定位; // 跳到未加载的旧轮次时用 before_idx 拉居中窗口再滚过去。GET /v1/tasks/{id}/outline。 outline: [], // task list 滚动加载 + 筛选 taskPage: 0, // 已加载到的最后一页(0 = 未加载) taskPageSize: 20, taskTotal: 0, taskLoaded: 0, // 已渲染条数(用于 has-more 判断) taskLoading: false, // 在途请求标记,防 observer 重复触发 taskHasMore: true, // 模型清单(GET /v1/models 一次缓存):新建对话框 + 顶栏切换下拉 + 历史小标显示名都用 models: [], // 图像生成模型清单(GET /v1/image_models;ARK_API_KEY 未设也会拿到 yaml 元数据) imageModels: [], // 当前选中的图像生成 variant key(per-session,不入 DB);默认 = imageModels[0].variant // (=yaml 第一个 = agent_builder fallback)。下次 send 消息时随 POST body 带给 backend。 imageModel: "", // 视频生成模型清单(GET /v1/video_models)+ 当前选中 variant。同 imageModels 范式。 videoModels: [], videoModel: "", // 润色按钮进行中标记:防止双击,同时让 syncOptimizeBtn 在 in-flight 期间不覆盖 // disabled 状态(否则用户键入 input 会把按钮从"润色中"误启回 enabled) optimizing: false, };