feat: tighten playbook and recorder landings
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-04 07:16:24 +08:00
parent 5e38ed39aa
commit 96446a25df
3 changed files with 63 additions and 6 deletions

View File

@@ -4,6 +4,12 @@
## 2026-04-04
### Playbook 与录制维护落点继续收口
- `创建 Agent / 编辑 Agent` 成功后,现在会直接回到 `Agent -> 当前 Agent / Agent 列表` 工作区,并把刚保存的 Agent 聚焦出来,不再只停在通用成功提示。
- `新增录制源 / 编辑录制源 / 导入 URL 配置 / 启停录制源 / 删除录制源` 成功后,都会统一回到 `生产中心 -> 录制维护`,让用户顺着同一个维护区继续做下一步。
- `当前 Agent` 面板新增显式锚点Agent 列表项补了稳定的 `data-assistant-id`,前端回归也补齐到了这两条业务流,避免后续又退回成“成功了但要自己找结果”。
### 主 Agent 配置与执行落点继续收口
- 发现页里三类关键动作现在会落到更精确的业务区域:账号分析会直接切到快照/字段/报告区域,高分作品分析会直接滚到“最近高分拆解”,相似账号生成会直接滚到“相似对标 / 已绑关系”。

View File

@@ -5651,6 +5651,29 @@ function focusRecentGeneratedCopy() {
});
}
function focusPlaybookWorkspace(assistantId = "") {
appState.playbookDetailTab = "workspace";
if (assistantId) appState.selectedAssistantId = assistantId;
setScreen("playbook");
renderAll();
window.requestAnimationFrame(() => {
const selector = assistantId
? `[data-assistant-id="${String(assistantId).replaceAll('"', '\\"')}"]`
: "#current-agent-anchor";
(document.querySelector(selector) || document.getElementById("current-agent-anchor"))
?.scrollIntoView({ behavior: "smooth", block: "start" });
});
}
function focusLiveRecorderMaintenance() {
appState.productionDetailTab = "recorder";
setScreen("production");
renderAll();
window.requestAnimationFrame(() => {
document.getElementById("live-recorder-maintenance-anchor")?.scrollIntoView({ behavior: "smooth", block: "start" });
});
}
function focusReviewWorkspace(reviewId = "") {
appState.reviewFocusId = reviewId || "";
setScreen("review");
@@ -6507,6 +6530,7 @@ function renderPlaybookScreen() {
})}
</div>
<div class="panel pad" style="box-shadow:none; margin-top:18px;">
<div id="current-agent-anchor"></div>
<div class="panel-head">
<div>
<h3>当前 Agent</h3>
@@ -6533,7 +6557,7 @@ function renderPlaybookScreen() {
<div class="panel-head"><div><h3>Agent 列表</h3><div class="panel-subtitle">当前接的是后端 assistants</div></div></div>
<div class="list">
${assistants.map((assistant) => `
<div class="task-item ${assistant.id === currentAssistant?.id ? "active" : ""}">
<div class="task-item ${assistant.id === currentAssistant?.id ? "active" : ""}" data-assistant-id="${escapeHtml(assistant.id)}">
<h4>${escapeHtml(assistant.name)}</h4>
<p>${escapeHtml(assistant.description || assistant.generation_goal || "暂无说明")}</p>
<div class="task-meta">
@@ -9804,6 +9828,7 @@ function openCreateAssistantAction() {
appState.selectedAssistantId = assistant.id;
rememberAction("Agent 已创建", `已创建 Agent「${assistant.name}」。`, "green", assistant);
await bootstrap();
focusPlaybookWorkspace(assistant.id);
}
});
}
@@ -9842,6 +9867,7 @@ function openEditAssistantAction(assistantId = "") {
appState.selectedAssistantId = updated.id;
rememberAction("Agent 已更新", `已更新 Agent「${updated.name}」。`, "green", updated);
await bootstrap();
focusPlaybookWorkspace(updated.id);
}
});
}
@@ -10626,11 +10652,7 @@ function openCreateRealCutAction(defaults = {}) {
}
function openLiveRecorderAction() {
setScreen("production");
renderAll();
window.requestAnimationFrame(() => {
document.getElementById("live-recorder-maintenance-anchor")?.scrollIntoView({ behavior: "smooth", block: "start" });
});
focusLiveRecorderMaintenance();
}
function openLiveRecorderCreateAction() {
@@ -10677,6 +10699,7 @@ function openLiveRecorderCreateAction() {
}
rememberAction("直播录制已下发", "当前租户的直播源已经保存到服务端并同步到 NAS。", "green", { saved, started });
await bootstrap();
focusLiveRecorderMaintenance();
}
});
}
@@ -10731,6 +10754,7 @@ function openLiveRecorderSourceAction(sourceId) {
});
rememberAction("录制源已更新", `已保存「${saved.item?.title || source.title || "录制源"}」。`, "green", saved);
await bootstrap();
focusLiveRecorderMaintenance();
}
});
}
@@ -10763,6 +10787,7 @@ function openLiveRecorderImportAction() {
});
rememberAction("URL 配置已导入", `已导入 ${formatNumber(saved.count || 0)} 条录制源。`, "green", saved);
await bootstrap();
focusLiveRecorderMaintenance();
}
});
}
@@ -10784,6 +10809,7 @@ async function toggleLiveRecorderSourceAction(sourceId, nextEnabled) {
});
rememberAction(nextEnabled ? "录制源已启用" : "录制源已停用", `${source.title || source.source_url || "录制源"} 已更新。`, "green");
await bootstrap();
focusLiveRecorderMaintenance();
} finally {
setBusy(false, "");
}
@@ -10808,6 +10834,7 @@ async function deleteLiveRecorderSourceAction(sourceId) {
});
rememberAction("录制源已删除", `${source.title || source.source_url || "录制源"} 已从租户视图中移除。`, "green");
await bootstrap();
focusLiveRecorderMaintenance();
} finally {
setBusy(false, "");
}

View File

@@ -906,6 +906,30 @@ test("review actions return to the review workspace with the saved review in foc
assert.match(APP, /data-review-id="\$\{escapeHtml\(review\.id\)\}"/);
});
test("assistant actions return to the playbook workspace with the saved assistant in focus", () => {
const createAssistant = extractBetween(APP, "function openCreateAssistantAction()", "function openEditAssistantAction(assistantId = \"\")");
const editAssistant = extractBetween(APP, "function openEditAssistantAction(assistantId = \"\")", "function openAnalyzeSelectedAccountAction()");
assert.match(APP, /function focusPlaybookWorkspace\(assistantId = ""\)/);
assert.match(createAssistant, /focusPlaybookWorkspace\(assistant\.id\)/);
assert.match(editAssistant, /focusPlaybookWorkspace\(updated\.id\)/);
assert.match(APP, /id="current-agent-anchor"/);
assert.match(APP, /data-assistant-id="\$\{escapeHtml\(assistant\.id\)\}"/);
});
test("live recorder mutations return to the recorder maintenance panel after success", () => {
const createSource = extractBetween(APP, "function openLiveRecorderCreateAction()", "function openLiveRecorderSourceAction(sourceId)");
const editSource = extractBetween(APP, "function openLiveRecorderSourceAction(sourceId)", "function openLiveRecorderImportAction()");
const importSource = extractBetween(APP, "function openLiveRecorderImportAction()", "async function toggleLiveRecorderSourceAction(sourceId, nextEnabled)");
const toggleSource = extractBetween(APP, "async function toggleLiveRecorderSourceAction(sourceId, nextEnabled)", "async function deleteLiveRecorderSourceAction(sourceId)");
const deleteSource = extractBetween(APP, "async function deleteLiveRecorderSourceAction(sourceId)", "async function openLiveRecorderFileAction(fileId)");
assert.match(APP, /function focusLiveRecorderMaintenance\(\)/);
assert.match(createSource, /focusLiveRecorderMaintenance\(\)/);
assert.match(editSource, /focusLiveRecorderMaintenance\(\)/);
assert.match(importSource, /focusLiveRecorderMaintenance\(\)/);
assert.match(toggleSource, /focusLiveRecorderMaintenance\(\)/);
assert.match(deleteSource, /focusLiveRecorderMaintenance\(\)/);
});
test("main agent execution cards can jump to oneliner and platform profile history", () => {
const messages = extractBetween(APP, "function renderOneLinerMessagesHtml()", "function renderAutoConnectingScreen(screenTitle, nextStepText)");
assert.match(messages, /data-action="open-oneliner-profile-history"/);