diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js
index f9521b9..4d584c6 100644
--- a/web/storyforge-web-v4/assets/app.js
+++ b/web/storyforge-web-v4/assets/app.js
@@ -5477,6 +5477,18 @@ function renderDiscoveryScreen() {
${escapeHtml(getAccountName(selected) || "未选中")}
+
+
+ 下一步先做
+ ${escapeHtml(detailTabs.find((tab) => tab.value === activeTab)?.label || "账号概览")}
+
+
${escapeHtml(selected ? `先围绕 ${getAccountName(selected)} 做导入、分析和相似扩展。` : "先从账号池里选一个对象,再继续导入和分析。")}
+
+ ${actionTag("导入当前对标", "open-import-selected-account")}
+ ${actionTag("账号分析", "analyze-selected-account")}
+ ${actionTag("查相似", "open-similar-search")}
+
+
当前对标 ${escapeHtml(getAccountName(selected) || "未选中")}
${escapeHtml(importedSources.length ? `已接入 ${importedSources.length}` : "未接入项目")}
@@ -6043,6 +6055,31 @@ function renderProductionScreen() {
把队列、恢复、录制和产物拆开看,减少一次性信息量。
+
+
+ 当前工作流
+ ${escapeHtml(tabs.find((tab) => tab.value === activeTab)?.label || "生产队列")}
+
+
${escapeHtml(
+ activeTab === "recovery"
+ ? "先处理失败任务和可恢复项,再决定是否批量重开。"
+ : activeTab === "recorder"
+ ? "先确认录制服务和文件状态,再回到队列继续推进。"
+ : activeTab === "outputs"
+ ? "先看产物和作品,再决定是否回到复盘或继续生产。"
+ : "先看处理中任务,再把异常和产物安排到下一步。"
+ )}
+
+ ${activeTab === "recovery"
+ ? `${actionTag("批量恢复", "batch-recover-jobs")} ${actionTag("查看恢复记录", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="recovery"`)}`
+ : activeTab === "outputs"
+ ? `${actionTag("去复盘", "goto-review")} ${actionTag("查看产物", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="outputs"`)}`
+ : activeTab === "recorder"
+ ? `${actionTag("录制维护", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="recorder"`)} ${actionTag("交给主 Agent", "handoff-to-main-agent", productionHandoffAttrs)}`
+ : `${actionTag("批量恢复", "batch-recover-jobs")} ${actionTag("交给主 Agent", "handoff-to-main-agent", productionHandoffAttrs)}`
+ }
+
+
处理中 ${escapeHtml(formatNumber(activeJobs.length || jobs.filter((item) => item.status !== "completed").length))}
失败 ${escapeHtml(formatNumber(failedJobs.length))}
diff --git a/web/storyforge-web-v4/assets/styles.css b/web/storyforge-web-v4/assets/styles.css
index d0fca0d..5f65c16 100644
--- a/web/storyforge-web-v4/assets/styles.css
+++ b/web/storyforge-web-v4/assets/styles.css
@@ -1386,6 +1386,10 @@ select {
gap: 8px;
}
+.mobile-flow-focus-card {
+ display: none;
+}
+
.sheet-html {
border: 1px solid var(--line);
border-radius: 16px;
@@ -2189,6 +2193,42 @@ tbody tr:hover {
text-align: center;
}
+ .mobile-flow-focus-card {
+ display: grid;
+ gap: 10px;
+ margin-bottom: 14px;
+ padding: 13px 14px;
+ border: 1px solid rgba(79, 143, 238, 0.16);
+ border-radius: 16px;
+ background: linear-gradient(180deg, rgba(247, 251, 255, 0.98) 0%, rgba(238, 246, 255, 0.98) 100%);
+ }
+
+ .mobile-flow-focus-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ }
+
+ .mobile-flow-focus-head strong {
+ font-size: 13px;
+ line-height: 1.3;
+ color: var(--text);
+ }
+
+ .mobile-flow-focus-card p {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.55;
+ color: var(--muted);
+ }
+
+ .mobile-flow-focus-card .task-meta .tag {
+ flex: 1 1 calc(50% - 4px);
+ justify-content: center;
+ text-align: center;
+ }
+
.entity-cell {
align-items: flex-start;
}
@@ -2364,6 +2404,42 @@ tbody tr:hover {
text-align: center;
}
+ .mobile-flow-focus-card {
+ display: grid;
+ gap: 10px;
+ margin-bottom: 14px;
+ padding: 13px 14px;
+ border: 1px solid rgba(79, 143, 238, 0.16);
+ border-radius: 16px;
+ background: linear-gradient(180deg, rgba(247, 251, 255, 0.98) 0%, rgba(238, 246, 255, 0.98) 100%);
+ }
+
+ .mobile-flow-focus-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ }
+
+ .mobile-flow-focus-head strong {
+ font-size: 13px;
+ line-height: 1.3;
+ color: var(--text);
+ }
+
+ .mobile-flow-focus-card p {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.55;
+ color: var(--muted);
+ }
+
+ .mobile-flow-focus-card .task-meta .tag {
+ flex: 1 1 calc(50% - 4px);
+ justify-content: center;
+ text-align: center;
+ }
+
.integration-card-head {
flex-direction: column;
}
diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs
index 0a25710..9b8f3dc 100644
--- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs
+++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs
@@ -135,6 +135,21 @@ test("discovery and production screens expose compact mobile flow summaries", ()
assert.match(production, /失败/);
});
+test("discovery and production screens expose mobile focus cards with next-step actions", () => {
+ const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()");
+ const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()");
+ const cssMobile = extractBetween(CSS, "@media (max-width: 760px) {", "@media (max-width: 560px) {");
+
+ assert.match(discovery, /mobile-only mobile-flow-focus-card/);
+ assert.match(discovery, /下一步先做/);
+ assert.match(discovery, /导入当前对标/);
+ assert.match(discovery, /查相似/);
+ assert.match(production, /mobile-only mobile-flow-focus-card/);
+ assert.match(production, /当前工作流/);
+ assert.match(production, /批量恢复/);
+ assert.match(cssMobile, /\.mobile-flow-focus-card/);
+});
+
test("projects screen uses an adaptive project grid instead of a fixed three-column squeeze", () => {
const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab(");
assert.match(projects, /project-status-grid/);