diff --git a/CHANGELOG.md b/CHANGELOG.md index 103974c..bc0151c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -341,3 +341,15 @@ - `额度` 页和租户额度编辑弹层新增了 `套餐档位` 与 `预算预警阈值`,现在能直接按试用、增长、规模、自定义四档去配置项目套餐。 - 租户额度面板会直接展示当前套餐档位和预警阈值,便于把预算和动作池表达成正式产品能力,而不是只看裸配额数字。 - 不可自动恢复的失败任务现在会打开站内“处理建议”面板,直接给出补信息、查看详情或交给主 Agent 的下一步,而不是只停在失败提示。 + +### 项目切换入口统一 + +- 所有 `select-project` 入口现在都统一走 `applySelectedProject()`,不再一部分入口回到项目总台、一部分入口只原地刷新。 +- 项目卡、项目 sheet 和其他项目切换入口都会在切换后回到 `项目总台` 主工作区,保证切完项目就能直接继续当前项目推进。 + +### 页面口径继续去掉半成品表达 + +- `Agent`、模型设置、跟踪、对标关系、复盘这些页面里的“后续再补”口径继续改成当前就能执行的表达,页面语气更像正式产品。 +- `创建 Agent / 编辑 Agent` 里的系统提示词占位改成“可先留空,后面随时补充”,减少半成品感。 +- `作品与成片`、Agent 执行项默认说明里的“再补”字眼也一起收掉,统一成当前可直接推进的表达。 +- 前端回归新增了这批文案断言,避免旧的占位口径再回流到主工作台。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index fd18381..5aaeba6 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -4547,7 +4547,7 @@ function renderPlatformAgentPanel() { return `
${escapeHtml(item.name || item.platform_label)}
-
${escapeHtml(item.mission || item.notes || "先绑定执行 Agent,再补任务目标和方法论。")}
+
${escapeHtml(item.mission || item.notes || "先绑定执行 Agent,再完善任务目标和方法论。")}
${escapeHtml(item.status || "draft")} ${item.readiness_label ? `= 50 ? "blue" : "orange"}">${escapeHtml(item.readiness_label)} ${escapeHtml(formatNumber(item.readiness_score || 0))}` : ""} @@ -6685,7 +6685,7 @@ function renderPlaybookScreen() {

当前 Agent

-
后续文案生成、对标绑定和复盘默认都会优先使用这里选中的 Agent。
+
文案生成、对标绑定和复盘都会优先使用这里选中的 Agent。
${currentAssistant ? `已选` : `未选`} @@ -7089,7 +7089,7 @@ function renderProductionScreen() {
-

作品与成片

先看真实作品,再补文档与成片
+

作品与成片

先看真实作品,再继续整理文档与成片
${works.map((video) => `
@@ -7865,7 +7865,7 @@ function openPreferredModelAction() { const gatewayModels = safeArray(localCatalog.models).map((item) => item.id).filter(Boolean); openActionModal({ title: "设置分析主模型", - description: "后续导入分析、市场调研和风格学习会优先使用这里设置的模型。", + description: "导入分析、市场调研和风格学习都会优先使用这里设置的模型。", submitLabel: "保存模型", fields: [ { @@ -8762,7 +8762,7 @@ function openTrackSelectedAccountAction() { title: trackedItem ? "更新跟踪账号" : "加入跟踪", description: trackedItem ? "这个账号已经在跟踪中,可以切换负责 Agent 或补充备注。" - : "把当前对标账号加入每日跟踪,后续自动生成更新日报。", + : "把当前对标账号加入每日跟踪,系统会自动生成更新日报。", submitLabel: trackedItem ? "保存跟踪" : "开始跟踪", fields: [ { name: "accountName", label: "账号", type: "html", html: `
${escapeHtml(getAccountName(account) || "未命名账号")}

${escapeHtml(getAccountProfileUrl(account) || account.signature || "")}

` }, @@ -9343,7 +9343,7 @@ function openSystemMainPolicyAction() { const current = bundle.current_version || {}; openActionModal({ title: "编辑系统主 Agent 策略", - description: "这是所有用户共享的系统级主 Agent 基座能力,后续用户层和管理员覆盖都会叠加在它上面。", + description: "这是所有用户共享的系统级主 Agent 基座能力,用户层和管理员覆盖都会叠加在它上面。", submitLabel: "保存系统策略", fields: [ { type: "html", label: "当前版本", html: renderPolicyVersionSummary(bundle, "系统主 Agent 还没有系统默认策略。") }, @@ -10098,7 +10098,7 @@ function openCreateAssistantAction() { { name: "name", label: "名称", placeholder: "例如:创业成交助手" }, { name: "description", label: "说明", placeholder: "例如:服务创业 IP 与成交型短视频" }, { name: "goal", label: "生成目标", placeholder: "例如:输出创业口播、对标拆解和成交文案" }, - { name: "systemPrompt", label: "系统提示词", type: "textarea", rows: 5, placeholder: "可选,不填则后续再补" }, + { name: "systemPrompt", label: "系统提示词", type: "textarea", rows: 5, placeholder: "可选,可先留空,后面随时补充" }, { name: "knowledgeBaseId", label: "默认知识库", type: "select", value: kbOptions[0]?.value || "", options: [{ value: "", label: "暂不绑定" }, ...kbOptions] }, { name: "modelProfileId", label: "主模型", type: "select", value: modelOptions.find((item) => item.value === safeArray(appState.dashboard?.model_profiles).find((m) => m.is_default)?.id)?.value || modelOptions[0]?.value || "", options: modelOptions } ], @@ -10141,7 +10141,7 @@ function openEditAssistantAction(assistantId = "") { { name: "name", label: "名称", value: assistant.name || "", placeholder: "例如:创业成交助手" }, { name: "description", label: "说明", value: assistant.description || "", placeholder: "例如:服务创业 IP 与成交型短视频" }, { name: "goal", label: "生成目标", value: assistant.generation_goal || "", placeholder: "例如:输出创业口播、对标拆解和成交文案" }, - { name: "systemPrompt", label: "系统提示词", type: "textarea", rows: 5, value: assistant.system_prompt || "", placeholder: "可选,不填则后续再补" }, + { name: "systemPrompt", label: "系统提示词", type: "textarea", rows: 5, value: assistant.system_prompt || "", placeholder: "可选,可先留空,后面随时补充" }, { name: "modelProfileId", label: "主模型", type: "select", value: assistant.model_profile_id || modelOptions[0]?.value || "", options: modelOptions } ], onSubmit: async (values) => { @@ -10289,7 +10289,7 @@ function openBenchmarkLinkAction(defaults = {}) { : null; openActionModal({ title: "保存对标关系", - description: "把当前账号和另一个账号关联成对标关系,便于后续持续跟踪。", + description: "把当前账号和另一个账号关联成对标关系,便于持续跟踪。", submitLabel: "保存关系", fields: [ { name: "targetAccountId", label: "目标账号", type: "select", value: defaults.targetAccountId || candidate?.candidate_account_id || options[0]?.value || "", options: [{ value: "", label: "仅保存主页链接" }, ...options] }, @@ -11220,7 +11220,7 @@ function openReviewAction(defaults = {}) { title: existingReview ? "编辑复盘" : "写复盘", description: existingReview ? "补充表现数据、判断和下一步动作,持续迭代项目策略。" - : "把完成任务写成一条可追踪复盘,后续可按项目累计。", + : "把完成任务写成一条可追踪复盘,可按项目累计。", submitLabel: existingReview ? "保存复盘" : "创建复盘", fields: [ { name: "title", label: "标题", value: existingReview?.title || defaults.title || sourceJob?.title || "", placeholder: "例如:创业口播 3 月 22 日复盘" }, @@ -11891,20 +11891,11 @@ document.addEventListener("click", async (event) => { return; } if (name === "select-project") { - appState.selectedProjectId = action.dataset.projectId || ""; - setBusy(true, "正在切换项目视图..."); try { - await loadStorageStatus(appState.selectedProjectId || ""); - await loadAgentControlSurfaces(appState.selectedProjectId || ""); - if (appState.selectedOnelinerSessionId) { - await loadOneLinerMessages(appState.selectedOnelinerSessionId); - } else { - appState.onelinerMessages = []; - } - } finally { - setBusy(false, ""); + await applySelectedProject(action.dataset.projectId || ""); + } catch (error) { + presentActionFailure(error, "切换项目失败"); } - renderAll(); return; } if (name === "select-platform") { diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index 4bbc1f7..5106faf 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -135,6 +135,7 @@ test("project creation and switching use in-app sheets instead of browser prompt test("mobile project sheets support direct project picking and zoom-safe form controls", () => { const projectSwitcher = extractBetween(APP, "function openDashboardProjectSwitcher()", "function openDashboardActionReasonAction("); const applySelectedProject = extractBetween(APP, "async function applySelectedProject(projectId = \"\")", "function openDashboardProjectSwitcher()"); + const clickHandler = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); const createProject = extractBetween(APP, "async function createProject()", "function openPreferredModelAction()"); assert.match(APP, /async function applySelectedProject\(projectId = ""\)/); assert.match(APP, /function focusDashboardWorkspace\(anchorId = "dashboard-workspace-anchor"\)/); @@ -150,6 +151,7 @@ test("mobile project sheets support direct project picking and zoom-safe form co assert.match(applySelectedProject, /loadStorageStatus\(appState\.selectedProjectId \|\| ""\)/); assert.match(applySelectedProject, /loadAgentControlSurfaces\(appState\.selectedProjectId \|\| ""\)/); assert.match(applySelectedProject, /focusDashboardWorkspace\("dashboard-workspace-anchor"\)/); + assert.match(clickHandler, /name === "select-project"[\s\S]*await applySelectedProject\(action\.dataset\.projectId \|\| ""\)/); assert.match(APP, /id="dashboard-workspace-anchor"/); assert.match(createProject, /onOpen:\s*\(/); assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.field-stack input,\s*[\s\S]*\.field-stack textarea,\s*[\s\S]*\.field-stack select\s*\{[\s\S]*min-height:\s*46px/); @@ -385,6 +387,17 @@ test("discovery and production screens expose compact mobile flow summaries", () assert.match(production, /失败/); }); +test("workbench copy uses direct live language instead of future-placeholder wording", () => { + assert.doesNotMatch(APP, /可选,不填则后续再补/); + assert.match(APP, /可选,可先留空,后面随时补充/); + assert.match(APP, /文案生成、对标绑定和复盘都会优先使用这里选中的 Agent/); + assert.match(APP, /导入分析、市场调研和风格学习都会优先使用这里设置的模型/); + assert.match(APP, /系统会自动生成更新日报/); + assert.match(APP, /把完成任务写成一条可追踪复盘,可按项目累计/); + assert.match(APP, /先绑定执行 Agent,再完善任务目标和方法论/); + assert.match(APP, /先看真实作品,再继续整理文档与成片/); +}); + 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()");