diff --git a/docs/superpowers/plans/2026-03-28-workbench-pages-usability-cleanup.md b/docs/superpowers/plans/2026-03-28-workbench-pages-usability-cleanup.md
new file mode 100644
index 0000000..4625b79
--- /dev/null
+++ b/docs/superpowers/plans/2026-03-28-workbench-pages-usability-cleanup.md
@@ -0,0 +1,85 @@
+# Workbench Pages Usability Cleanup Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Reduce cognitive overload across all non-dashboard workbench pages while preserving the current StoryForge UI style and business logic.
+
+**Architecture:** Keep the existing single-page shell and page render functions, but add lightweight page-level tab state, restore the missing settings route, and move admin-grade sections out of user pages. Prefer small render helpers and targeted page restructuring over a full rewrite.
+
+**Tech Stack:** Vanilla JavaScript SPA, static HTML, CSS, Node built-in test runner, Playwright for live verification.
+
+---
+
+### Task 1: Add regression coverage for page routing and scope boundaries
+
+**Files:**
+- Create: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/workbench-pages.test.mjs`
+
+- [ ] Write source-level regression tests for:
+ - settings nav route exists
+ - settings screen exists
+ - `renderAll()` renders settings
+ - automation page no longer renders quota / registry / admin ops
+ - Agent page no longer renders quota / registry
+ - discovery / production / admin pages expose page-tab interactions
+
+### Task 2: Restore settings as a real page
+
+**Files:**
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/index.html`
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+
+- [ ] Add `data-screen-target="settings"` to the sidebar button.
+- [ ] Add a real `data-screen="settings"` section.
+- [ ] Implement `renderSettingsScreen()` and include it in `renderAll()`.
+
+### Task 3: Add shared page-tab state and event handling
+
+**Files:**
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
+
+- [ ] Add page-level tab state to `appState`.
+- [ ] Add a small helper to render page tabs consistently.
+- [ ] Add click handling for `select-page-tab`.
+
+### Task 4: Simplify the heaviest user pages
+
+**Files:**
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
+
+- [ ] Refactor `renderDiscoveryScreen()` to use tabs for overview / snapshots / similar.
+- [ ] Refactor `renderProductionScreen()` to use tabs for queue / recovery / recorder / outputs.
+- [ ] Refactor `renderAutomationScreen()` to use tabs and remove admin-grade sections.
+- [ ] Refactor `renderPlaybookScreen()` to use tabs and remove quota / registry from the user page.
+
+### Task 5: Strengthen thin pages and compress medium pages
+
+**Files:**
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
+
+- [ ] Expand `renderOwnedScreen()` into a usable account workbench.
+- [ ] Expand `renderCreditsScreen()` into a readable quota page.
+- [ ] Lightly tune `renderProjectsScreen()` and `renderReviewScreen()` to reduce raw data feel and repetition.
+
+### Task 6: Convert admin workbench into a tabbed control surface
+
+**Files:**
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
+
+- [ ] Refactor `renderAdminWorkbenchScreen()` to use tabs for integrations / storage / agents / ops.
+- [ ] Keep all system governance sections here instead of user pages.
+
+### Task 7: Verify locally and on the NAS page
+
+**Files:**
+- None
+
+- [ ] Run `node --test /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/dashboard-home.test.mjs /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/workbench-pages.test.mjs`
+- [ ] Run `node --check /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
+- [ ] Run `bash /Users/kris/code/StoryForge-gitea/scripts/check_repo_baseline.sh`
+- [ ] Redeploy the NAS frontend if needed.
+- [ ] Re-check the key pages in a real browser and confirm no new console errors.
diff --git a/docs/superpowers/specs/2026-03-28-workbench-pages-usability-cleanup-design.md b/docs/superpowers/specs/2026-03-28-workbench-pages-usability-cleanup-design.md
new file mode 100644
index 0000000..c4935f4
--- /dev/null
+++ b/docs/superpowers/specs/2026-03-28-workbench-pages-usability-cleanup-design.md
@@ -0,0 +1,134 @@
+# 非首页工作台页面可用性整改设计
+
+## 目标
+
+在不改变当前 StoryForge Web V4 整体视觉风格的前提下,整改首页之外的工作台页面,让页面更符合人类使用逻辑:
+
+- 信息更容易扫读
+- 同页只处理一类任务
+- 普通用户更容易上手
+- 管理员页保留专业度,但不再是长页面堆叠
+
+## 范围
+
+本轮只处理首页之外的页面:
+
+- 我的项目
+- 找对标
+- 跟踪账号
+- 我的账号
+- Agent
+- 生产中心
+- 发布与复盘
+- 自动流程
+- 额度
+- 管理员配置台
+- 设置
+
+首页不在本轮范围内。
+
+## 设计原则
+
+1. 不换皮,只做信息减法和层级重排。
+2. 一个页面只允许一个主任务视角,深层信息通过页内 tab 切换。
+3. 系统治理内容从普通用户页面移走,优先归入管理员配置台。
+4. 太空的页面要补到能独立成立,太重的页面要压缩成单主区。
+
+## 页面级方案
+
+### 设置
+
+- 修复现有切页异常,点击后必须真正进入设置页。
+- 新增轻量设置页,展示:
+ - 当前连接状态
+ - 当前工作区 / 当前项目
+ - 自动连接说明
+ - 页面使用偏好与帮助入口
+- 超级管理员在这里看到“去管理员配置台”的入口提示,但不把管理员配置内容塞进设置页。
+
+### 自动流程
+
+- 只保留普通用户真正需要理解的两层:
+ - 依赖健康
+ - 动作防呆
+- 租户额度、OneLiner 动作注册表、运维审计从本页移走。
+- 页面结构改成页内 tab,一次只看一个区块。
+
+### Agent
+
+- 普通用户 Agent 页只保留:
+ - OneLiner 主 Agent 使用区
+ - 当前 Agent
+ - 平台 Agent 协作状态
+ - 模型/学习/最近生成
+- 租户额度和 OneLiner 动作注册表移出本页。
+- 页面改成页内 tab,降低一次性信息密度。
+
+### 找对标
+
+- 保留当前业务逻辑,不删能力。
+- 但把深层信息改成页内 tab,一次只展示一种视角:
+ - 账号概览
+ - 快照 / 字段 / 报告
+ - 相似对标 / 已绑关系
+- 账号列表和当前选中账号仍保留在主结构里。
+
+### 生产中心
+
+- 页面改成页内 tab:
+ - 生产队列
+ - 失败恢复
+ - 录制维护
+ - 作品与产物
+- 当前页顶部保留总览和主动作,不再把四大块内容同时展开。
+
+### 管理员配置台
+
+- 保留系统级治理边界:
+ - 依赖健康
+ - 存储状态
+ - 平台 Agent / OneLiner 系统级管理
+ - 运维与审计
+- 页面改成页内 tab,避免长页面堆叠。
+
+### 我的账号
+
+- 从“单张摘要卡”补成可成立的工作页面:
+ - 当前身份
+ - 当前负责项目与 Agent
+ - 最近工作摘要
+ - 常用快捷动作
+
+### 额度
+
+- 从“几个孤立数字”补成更接近产品化额度页:
+ - 当前额度摘要
+ - 已用 / 剩余 / 预估
+ - 套餐化解释
+ - 风险提示
+
+### 我的项目 / 跟踪账号 / 发布与复盘
+
+- 保持现有方向,不大改视觉骨架。
+- 只做轻量压缩:
+ - 我的项目:弱化原始导入队列感,强调下一步动作
+ - 跟踪账号:增强空状态可理解性
+ - 发布与复盘:减少重复项,强调“待补复盘”
+
+## 信息边界
+
+普通用户页面不再承接这些系统级内容:
+
+- OneLiner 动作注册表
+- 租户额度与审计全量治理面板
+- 运维与审计 Agent 全量事件墙
+
+这些内容统一收口到管理员配置台。
+
+## 验收标准
+
+- 设置页可以真实切换并渲染独立内容。
+- 自动流程、Agent、找对标、生产中心、管理员配置台都改成“一次只展开一类信息”的结构。
+- 自动流程与 Agent 页面不再混入管理员治理内容。
+- 我的账号和额度页面不再显得像半成品空页。
+- 控制台无新增报错,现有 NAS 页面可正常浏览。
diff --git a/scripts/check_repo_baseline.sh b/scripts/check_repo_baseline.sh
index 9fdaf9e..4442d17 100755
--- a/scripts/check_repo_baseline.sh
+++ b/scripts/check_repo_baseline.sh
@@ -39,7 +39,9 @@ for file in web/storyforge-web-v4/assets/app.js web/storyforge-web-v4/assets/sto
done
node --check scripts/douyin-browser-capture/control_panel.mjs
-echo "[5/5] validate homepage dashboard tests"
-node --test web/storyforge-web-v4/tests/dashboard-home.test.mjs
+echo "[5/5] validate homepage and workbench tests"
+node --test \
+ web/storyforge-web-v4/tests/dashboard-home.test.mjs \
+ web/storyforge-web-v4/tests/workbench-pages.test.mjs
echo "baseline checks passed"
diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js
index 6052fda..d549c4f 100644
--- a/web/storyforge-web-v4/assets/app.js
+++ b/web/storyforge-web-v4/assets/app.js
@@ -28,6 +28,12 @@ const appState = {
currentPlatform: localStorage.getItem(STORAGE_KEY + ":currentPlatform") || "",
selectedProjectId: "",
dashboardOverviewTab: "project_progress",
+ discoveryDetailTab: "overview",
+ playbookDetailTab: "workspace",
+ productionDetailTab: "queue",
+ automationDetailTab: "health",
+ adminWorkbenchTab: "integrations",
+ settingsDetailTab: "workspace",
selectedAssistantId: "",
lastSeenAt: SESSION_STORE.getLastSeenAt(Date.now()),
trackingCursorMap: {},
@@ -631,16 +637,25 @@ function setBusy(next, message = "") {
renderAuthUi();
}
-function setScreen(id) {
- appState.screen = id;
+function getScreenFromHash() {
+ const next = window.location.hash.replace("#", "");
+ return screenMap[next] ? next : "dashboard";
+}
+
+function setScreen(id, options = {}) {
+ const { updateHash = true } = options;
+ const resolvedId = screenMap[id] ? id : "dashboard";
+ appState.screen = resolvedId;
navButtons.forEach((button) => {
- const active = button.dataset.screenTarget === id;
+ const active = button.dataset.screenTarget === resolvedId;
button.classList.toggle("is-active", active);
});
screens.forEach((screen) => {
- screen.classList.toggle("is-active", screen.dataset.screen === id);
+ screen.classList.toggle("is-active", screen.dataset.screen === resolvedId);
});
- window.location.hash = id;
+ if (updateHash && window.location.hash !== `#${resolvedId}`) {
+ window.location.hash = resolvedId;
+ }
}
function ensureAuthUi() {
@@ -3829,16 +3844,34 @@ function renderAdminWorkbenchScreen() {
renderEmptyState("无权限", "请使用超级管理员账号访问管理员配置台。")
);
}
+ const tabs = [
+ { value: "integrations", label: "依赖健康" },
+ { value: "storage", label: "存储状态" },
+ { value: "agents", label: "Agent 治理" },
+ { value: "ops", label: "运维审计" }
+ ];
+ const activeTab = getActiveDetailTab("adminWorkbenchTab", tabs);
return screenShell(
"管理员配置台",
"系统级依赖、存储、平台 Agent 与运维治理。",
"",
`
- ${renderIntegrationOverviewPanel({ showActions: false })}
-
${renderStorageStatusPanel()}
- ${renderPlatformAgentPanel()}
- ${renderOneLinerActionRegistryPanel()}
- ${renderAdminOpsPanel()}
+
+
+
+
系统治理工作区
+
按系统依赖、存储、Agent 治理和运维审计分区查看,不再整页堆叠。
+
+
+ ${renderDetailTabs("adminWorkbenchTab", tabs)}
+ ${activeTab === "integrations"
+ ? renderIntegrationOverviewPanel({ showActions: false })
+ : activeTab === "storage"
+ ? renderStorageStatusPanel()
+ : activeTab === "agents"
+ ? `${renderPlatformAgentPanel()}
${renderOneLinerActionRegistryPanel()}
`
+ : renderAdminOpsPanel()}
+
`
);
}
@@ -3934,6 +3967,146 @@ function renderProjectsScreen() {
);
}
+function getActiveDetailTab(stateKey, tabs) {
+ const fallback = tabs[0]?.value || "";
+ const values = tabs.map((item) => item.value);
+ const active = values.includes(appState[stateKey]) ? appState[stateKey] : fallback;
+ appState[stateKey] = active;
+ return active;
+}
+
+function renderDetailTabs(stateKey, tabs) {
+ const active = getActiveDetailTab(stateKey, tabs);
+ return `
+
+ ${tabs.map((tab) => `
+
+ `).join("")}
+
+ `;
+}
+
+function renderDiscoveryOverviewSection({ selected, selectedProject, importedSources, tracked, workbenchReason, topVideos, reports, latestVideos, currentPlatformLabel }) {
+ return `
+
+
+
+
接入当前项目
把当前对标导入到项目,并绑定 Agent 做持续同步
${escapeHtml(importedSources.length ? "已接入" : "未接入")}
+ ${selected ? `
+
+
${escapeHtml(selectedProject?.name || "未选项目")}
+
${escapeHtml(importedSources.length ? `当前项目已接入 ${formatNumber(importedSources.length)} 个内容源,可继续同步或换 Agent。` : "当前项目还没有接入这个对标账号,可直接导入主页并绑定 Agent。")}
+
+ ${escapeHtml(selectedProject?.name || "未选项目")}
+ ${escapeHtml(getSelectedAssistant()?.name || "未选 Agent")}
+ ${actionTag(importedSources.length ? "继续同步" : "导入当前对标", "open-import-selected-account", "", { disabledReason: workbenchReason || "" })}
+ ${tracked ? `已在跟踪` : actionTag("加入跟踪", "open-track-selected-account", "", { disabledReason: workbenchReason || "" })}
+
+
+ ` : `
还没有选中账号
先从上方列表选一个对标账号,再决定是否导入到当前项目。
`}
+
+
+
+
账号画像
+
+ - ${escapeHtml(selected?.signature || "暂无签名")}
+ - ${escapeHtml("平台:" + currentPlatformLabel)}
+ - ${escapeHtml("标签:" + (safeArray(selected?.tags).slice(0, 4).join(" / ") || "暂无标签"))}
+ - ${escapeHtml("同步状态:" + (selected?.sync_status || "-"))}
+
+
+
+
高分作品
+
+ ${topVideos.map((video) => `- ${escapeHtml(describeVideo(video))}
`).join("") || "- 暂无高分作品
"}
+
+
+
+
最近报告
+
+ ${reports.slice(0, 3).map((report) => {
+ const suggestion = safeArray(report.suggestions)[0];
+ const summary = suggestion?.parsed_json?.executive_summary || suggestion?.suggestion_text || report.focus_text || "暂无结论";
+ return `- ${escapeHtml(brief(summary, 48))}
`;
+ }).join("") || "- 暂无分析报告
"}
+
+
+
+
+
+
+
${escapeHtml(formatNumber(latestVideos.length))} 条
+
+ ${latestVideos.map((video) => `
+
+
${escapeHtml(describeVideo(video))}
+
发布时间 ${escapeHtml(formatDateTime(video.published_at))} · 播放 ${escapeHtml(formatNumber(video.stats?.play))} · 点赞 ${escapeHtml(formatNumber(video.stats?.like))}
+
+
+ `).join("") || `
还没有最近作品
当前账号只同步了基础信息,还没拉到完整作品列表。
`}
+
+
+
+
+ `;
+}
+
+function renderDiscoveryRelationsSection(linkedAccounts, similarCandidates) {
+ return `
+
+
+
+
${escapeHtml(formatNumber(linkedAccounts.length))} 个
+
+ ${linkedAccounts.map((link) => `
+
+
${escapeHtml(link.target_nickname || link.target_profile_url || "未命名对标")}
+
${escapeHtml(link.note || link.target_profile_url || "已保存对标关系")}
+
+
+ `).join("") || `
`}
+
+
+
+
+
+
${escapeHtml(formatNumber(similarCandidates.length))} 个
+
+ ${similarCandidates.map((candidate, index) => `
+
+
${escapeHtml(candidate.candidate_nickname || candidate.candidate_profile_url || "候选账号")}
+
${escapeHtml(brief(candidate.rationale_text || "暂无理由", 96))}
+
+
+ `).join("") || `
还没有相似候选
先点“查相似”,这里会展示最近一轮结果。
`}
+
+
+
+
+ `;
+}
+
function renderDiscoveryScreen() {
if (!appState.dashboard) {
return screenShell("找对标", "完成工作区自动连接后才能加载真实对标账号。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("对标库未加载", "自动连接成功后,这里会显示当前平台的账号列表和详情。"));
@@ -3963,6 +4136,47 @@ function renderDiscoveryScreen() {
const selectedProject = getSelectedProject();
const importedSources = getCurrentProjectSourcesForAccount(selected, selectedProject?.id || "");
const tracked = selected?.id ? isTrackedAccount(selected.id) : false;
+ const detailTabs = [
+ { value: "overview", label: "账号概览" },
+ { value: "snapshots", label: "快照 / 字段 / 报告" },
+ { value: "relations", label: "相似对标 / 已绑关系" }
+ ];
+ const activeTab = getActiveDetailTab("discoveryDetailTab", detailTabs);
+ const selectedSummaryHtml = `
+
+
+
${escapeHtml(initials(getAccountName(selected) || "SF"))}
+
+
${escapeHtml(getAccountName(selected) || "还没有选中账号")}
+
${escapeHtml(getAccountProfileUrl(selected) || selected?.signature || "先从上方列表选一个账号,这里会展示当前对象。")}
+
+
+
+
作品数${escapeHtml(formatNumber(selected?.video_summary?.count))}
+
高分作品${escapeHtml(formatNumber(topVideos.length))}
+
报告数${escapeHtml(formatNumber(reports.length))}
+
已绑对标${escapeHtml(formatNumber(linkedAccounts.length))}
+
+
+ `;
+ let detailBodyHtml = "";
+ if (activeTab === "overview") {
+ detailBodyHtml = renderDiscoveryOverviewSection({
+ selected,
+ selectedProject,
+ importedSources,
+ tracked,
+ workbenchReason,
+ topVideos,
+ reports,
+ latestVideos,
+ currentPlatformLabel
+ });
+ } else if (activeTab === "snapshots") {
+ detailBodyHtml = renderDouyinInsightPanel();
+ } else {
+ detailBodyHtml = renderDiscoveryRelationsSection(linkedAccounts, similarCandidates);
+ }
return screenShell(
"找对标",
isWorkbenchPlatform(currentPlatform)
@@ -4049,127 +4263,17 @@ function renderDiscoveryScreen() {
-
-
-
-
${escapeHtml(getAccountName(selected) || "未选中")}
-
-
-
${escapeHtml(initials(getAccountName(selected) || "SF"))}
-
-
${escapeHtml(getAccountName(selected) || "还没有选中账号")}
-
${escapeHtml(getAccountProfileUrl(selected) || selected?.signature || "左侧点一个账号,这里会展示详情。")}
-
-
-
- 作品 ${escapeHtml(formatNumber(selected?.video_summary?.count))}
- 高分 ${escapeHtml(formatNumber(topVideos.length))}
- 报告 ${escapeHtml(formatNumber(reports.length))}
- 对标 ${escapeHtml(formatNumber(linkedAccounts.length))}
-
-
-
作品数${escapeHtml(formatNumber(selected?.video_summary?.count))}
-
高分作品${escapeHtml(formatNumber(topVideos.length))}
-
报告数${escapeHtml(formatNumber(reports.length))}
-
已绑对标${escapeHtml(formatNumber(linkedAccounts.length))}
-
-
-
-
接入当前项目
把当前对标导入到项目,并绑定 Agent 做持续同步
${escapeHtml(importedSources.length ? "已接入" : "未接入")}
- ${selected ? `
-
-
${escapeHtml(selectedProject?.name || "未选项目")}
-
${escapeHtml(importedSources.length ? `当前项目已接入 ${formatNumber(importedSources.length)} 个内容源,可继续同步或换 Agent。` : "当前项目还没有接入这个对标账号,可直接导入主页并绑定 Agent。")}
-
- ${escapeHtml(selectedProject?.name || "未选项目")}
- ${escapeHtml(getSelectedAssistant()?.name || "未选 Agent")}
- ${actionTag(importedSources.length ? "继续同步" : "导入当前对标", "open-import-selected-account", "", { disabledReason: workbenchReason || "" })}
- ${tracked ? `${escapeHtml("已在跟踪")}` : actionTag("加入跟踪", "open-track-selected-account", "", { disabledReason: workbenchReason || "" })}
-
-
- ` : `
还没有选中账号
先从左侧列表选一个对标账号,再决定是否导入到当前项目。
`}
-
-
-
-
账号画像
-
- - ${escapeHtml(selected?.signature || "暂无签名")}
- - ${escapeHtml("平台:" + currentPlatformLabel)}
- - ${escapeHtml("标签:" + safeArray(selected?.tags).slice(0, 4).join(" / ") || "暂无标签")}
- - ${escapeHtml("同步状态:" + (selected?.sync_status || "-"))}
-
-
-
-
高分作品
-
- ${topVideos.map((video) => `- ${escapeHtml(describeVideo(video))}
`).join("") || "- 暂无高分作品
"}
-
-
-
-
最近报告
-
- ${reports.slice(0, 3).map((report) => {
- const suggestion = safeArray(report.suggestions)[0];
- const summary = suggestion?.parsed_json?.executive_summary || suggestion?.suggestion_text || report.focus_text || "暂无结论";
- return `- ${escapeHtml(brief(summary, 48))}
`;
- }).join("") || "- 暂无分析报告
"}
-
-
-
-
-
${escapeHtml(formatNumber(effectiveVideos.length))} 条
-
- ${latestVideos.map((video) => `
-
-
${escapeHtml(describeVideo(video))}
-
发布时间 ${escapeHtml(formatDateTime(video.published_at))} · 播放 ${escapeHtml(formatNumber(video.stats?.play))} · 点赞 ${escapeHtml(formatNumber(video.stats?.like))}
-
-
- `).join("") || `
还没有最近作品
当前账号只同步了基础信息,还没拉到完整作品列表。
`}
-
-
- ${renderDouyinInsightPanel()}
-
-
-
-
-
-
- ${linkedAccounts.map((link) => `
-
-
${escapeHtml(link.target_nickname || link.target_profile_url || "未命名对标")}
-
${escapeHtml(link.note || link.target_profile_url || "已保存对标关系")}
-
-
- `).join("") || `
`}
-
-
-
-
${escapeHtml(formatNumber(similarCandidates.length))} 个
-
- ${similarCandidates.map((candidate, index) => `
-
-
${escapeHtml(candidate.candidate_nickname || candidate.candidate_profile_url || "候选账号")}
-
${escapeHtml(brief(candidate.rationale_text || "暂无理由", 96))}
-
-
- `).join("") || `
还没有相似候选
先点“查相似”,这里会展示最近一轮结果。
`}
-
+
+
+
+
当前选中对标
+
先看核心信息,再按分类切换深层内容。
+
${escapeHtml(getAccountName(selected) || "未选中")}
+ ${selectedSummaryHtml}
+ ${renderDetailTabs("discoveryDetailTab", detailTabs)}
+ ${detailBodyHtml}
`
@@ -4267,6 +4371,11 @@ function renderAutomationScreen() {
const aiVideoJobs = jobs.filter((item) => item.line_type === "ai_video").length;
const realCutJobs = jobs.filter((item) => item.line_type === "real_cut").length;
const overview = getIntegrationOverview();
+ const tabs = [
+ { value: "health", label: "依赖健康" },
+ { value: "guards", label: "动作防呆" }
+ ];
+ const activeTab = getActiveDetailTab("automationDetailTab", tabs);
return screenShell(
"自动流程",
"自动同步、日报生成和失败补跑先统一看这里。",
@@ -4282,35 +4391,37 @@ function renderAutomationScreen() {
内容源${escapeHtml(formatNumber(appState.contentSources.length))}
-
- ${renderIntegrationOverviewPanel({ showActions: false })}
-
-
+
-
动作防呆
-
依赖不可用时,相关动作会在这里和生产页一起被拦住。
+
自动链路工作区
+
普通用户只看健康状态和动作是否可执行,系统治理移到管理员配置台。
${escapeHtml(overview.headline)}
-
- AI 视频 ${escapeHtml(getPipelineGuard("aiVideo").enabled ? "可执行" : "已拦截")}
- 实拍剪辑 ${escapeHtml(getPipelineGuard("realCut").enabled ? "可执行" : "已拦截")}
- ASR ${escapeHtml(getIntegrationStatus(getIntegrationDetail("asr")).summary)}
-
-
- ${renderPipelineButton("aiVideo", "primary")}
- ${renderPipelineButton("realCut")}
-
-
${escapeHtml(overview.subtitle)}
+ ${renderDetailTabs("automationDetailTab", tabs)}
+ ${activeTab === "health" ? renderIntegrationOverviewPanel({ showActions: false }) : `
+
+
+
+
动作防呆
+
依赖不可用时,相关动作会在这里和生产页一起被拦住。
+
+
${escapeHtml(overview.headline)}
+
+
+ AI 视频 ${escapeHtml(getPipelineGuard("aiVideo").enabled ? "可执行" : "已拦截")}
+ 实拍剪辑 ${escapeHtml(getPipelineGuard("realCut").enabled ? "可执行" : "已拦截")}
+ ASR ${escapeHtml(getIntegrationStatus(getIntegrationDetail("asr")).summary)}
+
+
+ ${renderPipelineButton("aiVideo", "primary")}
+ ${renderPipelineButton("realCut")}
+
+
${escapeHtml(overview.subtitle)}
+
+ `}
-
- ${renderTenantQuotaPanel()}
-
-
- ${renderOneLinerActionRegistryPanel()}
-
- ${renderAdminOpsPanel()}
`
);
}
@@ -4321,6 +4432,10 @@ function renderOwnedScreen() {
}
const me = appState.me || appState.session?.account || {};
const firstAssistant = safeArray(appState.dashboard.assistants)[0];
+ const selectedProject = getSelectedProject();
+ const jobs = safeArray(appState.dashboard.recent_jobs);
+ const completedJobs = jobs.filter((item) => item.status === "completed").length;
+ const activeJobs = jobs.filter((item) => item.status !== "completed").length;
return screenShell(
"我的账号",
"这里先用当前登录账号和最近产出组合成第一版总览。",
@@ -4341,6 +4456,51 @@ function renderOwnedScreen() {
素材${escapeHtml(formatNumber(appState.documents.length))}
+
+
+
+
+
+
+
当前项目 · ${escapeHtml(selectedProject?.name || "未选项目")}
+
${escapeHtml(selectedProject?.description || "当前还没有清晰项目说明,可以去“我的项目”补齐。")}
+
+ 项目 ${escapeHtml(formatNumber(appState.dashboard.projects?.length))}
+ ${escapeHtml(getSelectedAssistant()?.name || "未选 Agent")}
+ ${actionTag("去我的项目", "goto-intake")}
+
+
+
+
当前主 Agent
+
${escapeHtml(firstAssistant?.generation_goal || firstAssistant?.description || "先在 Agent 页面补齐你的默认协作方式。")}
+
+ ${escapeHtml(firstAssistant?.name || "默认文案助手")}
+ ${actionTag("去 Agent", "goto-playbook")}
+
+
+
+
+
+
+
+
+
+
待推进任务${escapeHtml(formatNumber(activeJobs))}
+
已完成任务${escapeHtml(formatNumber(completedJobs))}
+
平台数${escapeHtml(formatNumber(getPlatformOptions().length))}
+
对标数${escapeHtml(formatNumber(appState.accounts.length))}
+
+
+
推荐下一步
+
${escapeHtml(activeJobs ? "先去生产中心处理待推进任务,再回到首页看下一条动作。" : "当前任务不重,适合补 Agent 策略或整理项目说明。")}
+
+ ${actionTag(activeJobs ? "去生产中心" : "去 Agent", activeJobs ? "goto-production" : "goto-playbook")}
+ ${actionTag("看跟踪账号", "goto-tracking")}
+
+
+
+
+
`
);
}
@@ -4355,6 +4515,12 @@ function renderPlaybookScreen() {
const currentAssistant = getSelectedAssistant();
const localCatalog = appState.localModelCatalog || {};
const gatewayModels = safeArray(localCatalog.models).map((item) => item.id).filter(Boolean);
+ const tabs = [
+ { value: "workspace", label: "当前 Agent 工作台" },
+ { value: "platform_agents", label: "平台 Agent" },
+ { value: "models", label: "模型与学习" }
+ ];
+ const activeTab = getActiveDetailTab("playbookDetailTab", tabs);
return screenShell(
"Agent",
"这里接真实 Agent 列表,当前已经支持切换和编辑 Agent。",
@@ -4370,129 +4536,144 @@ function renderPlaybookScreen() {
-
OneLiner 主 Agent
-
前端还没上的功能由它兜底承接,并调度平台 Agent。
-
-
-
-
${escapeHtml(appState.onelinerProfile?.long_term_goal || "还没有设置长期目标")}
-
${escapeHtml(appState.onelinerProfile?.notes || "你可以把用户长期目标、账号目标、默认平台都绑给 OneLiner,再让它去调度平台 Agent。")}
-
- 会话 ${escapeHtml(formatNumber(safeArray(appState.onelinerSessions).length))}
- 平台 Agent ${escapeHtml(formatNumber(safeArray(appState.platformAgents).length))}
- 编辑配置
-
-
-
-
- ${renderOneLinerActionRegistryPanel()}
-
-
- ${renderPlatformAgentPanel()}
-
-
- ${renderTenantQuotaPanel()}
-
-
-
-
-
当前 Agent
-
后续文案生成、对标绑定和复盘默认都会优先使用这里选中的 Agent
-
-
- ${currentAssistant ? `已选` : `未选`}
- ${currentAssistant ? `编辑` : ""}
-
-
- ${currentAssistant ? `
-
-
${escapeHtml(currentAssistant.name)}
-
${escapeHtml(currentAssistant.generation_goal || currentAssistant.description || "先补齐这个 Agent 的目标和说明。")}
-
- ${escapeHtml(models.find((item) => item.id === currentAssistant.model_profile_id)?.name || "默认模型")}
- ${escapeHtml(formatNumber(safeArray(currentAssistant.knowledge_base_ids).length))} 条知识库
- ${escapeHtml(brief(currentAssistant.description || "暂无说明", 22))}
-
-
- ` : `
还没有可用 Agent
先创建一个 Agent,再把当前项目的内容都交给它学习。
`}
-
-
-
-
-
本机模型网关
-
当前默认分析会优先走本机 cli-proxy-api
-
-
-
-
-
${escapeHtml(currentModel?.name || localCatalog.default_model || "GLM-5")}
-
${escapeHtml(currentModel ? `${currentModel.model_name || "-"} · ${currentModel.base_url || "-"}` : (localCatalog.public_base_url || localCatalog.base_url || "尚未读取到网关地址"))}
-
- ${gatewayModels.slice(0, 6).map((model) => `${escapeHtml(model)}`).join("") || `暂无可见模型`}
-
-
-
-
-
-
-
- ${models.map((model) => `
-
-
${escapeHtml(model.name)}
-
${escapeHtml(model.model_name || "-")} · ${escapeHtml(model.base_url || "-")}
-
- `).join("") || `
`}
-
-
-
-
Agent 列表
当前接的是后端 assistants
-
- ${assistants.map((assistant) => `
-
-
${escapeHtml(assistant.name)}
-
${escapeHtml(assistant.description || assistant.generation_goal || "暂无说明")}
-