From 4adb545e0df0735fa94e02e1c6f44f87825d25c4 Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 5 Apr 2026 11:48:05 +0800 Subject: [PATCH] feat: improve intake sheet context defaults --- CHANGELOG.md | 1 + web/storyforge-web-v4/assets/app.js | 34 +++++++++++++++---- .../tests/workbench-pages.test.mjs | 19 +++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adf1831..5cca789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -483,6 +483,7 @@ - 全局 `生成文案` 旧入口也已经做成相同分流:优先围绕最近完成任务直接生成,只有没有可承接任务时才回退到旧文案表单。 # 2026-04-05 +- intake: `导入作品 / 导入文本 / 上传视频` 现在会先显示当前项目、默认 Agent 和默认知识库的上下文摘要,并预填更贴近当前工作流的标题提示。 - intake: 遗留 `导入主页` 入口现在会优先复用当前选中对标的主页链接 direct-execute,只有缺少选中对象或主页链接时才回退到表单。 - agent: 遗留 `创建 Agent` 入口现在也会优先 direct-execute,当前项目已就绪时直接创建 Agent,只有缺上下文时才回退到旧表单。 - pipeline: 全局旧入口 `AI 视频 / 实拍剪辑` 现在也会优先围绕最近完成任务 direct-execute,只有没有可承接任务时才回退到旧表单。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index 4b3a7f6..b7c9093 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -356,6 +356,22 @@ function formatBytes(value) { return `${fixed}${units[idx]}`; } +function renderIntakeActionContextHtml(projectId = "", assistantId = "") { + const project = safeArray(appState.dashboard?.projects).find((item) => item.id === projectId) + || getSelectedProject(); + const assistant = safeArray(appState.dashboard?.assistants).find((item) => item.id === assistantId) + || getSelectedAssistant(); + const knowledgeBase = getProjectKnowledgeBases(project?.id || "")[0] || null; + return ` +
+
+

这次会写入哪里

+

项目:${escapeHtml(project?.name || "当前项目")} · Agent:${escapeHtml(assistant?.name || "暂不绑定")} · 知识库:${escapeHtml(knowledgeBase?.name || "默认知识库")}

+
+
+ `; +} + function formatDateTime(value) { if (!value) return "-"; const date = new Date(value); @@ -9177,15 +9193,17 @@ function openTrackSelectedAccountAction() { function openImportVideoLinkAction() { const project = requireSelectedProject(); const assistants = getAssistantOptions(project.id); + const defaultAssistantId = getSelectedAssistant()?.id || assistants[0]?.value || ""; openActionModal({ title: "导入作品链接", description: "直接把单条视频链接送进分析链。", submitLabel: "开始分析", fields: [ + { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, - { name: "title", label: "标题", placeholder: "可选,不填则使用默认标题" }, + { name: "title", label: "标题", placeholder: "例如:当前对标高分视频" }, { name: "videoUrl", label: "作品链接", type: "url", placeholder: "https://..." }, - { name: "assistantId", label: "绑定 Agent", type: "select", value: assistants[0]?.value || "", options: [{ value: "", label: "暂不绑定" }, ...assistants] }, + { name: "assistantId", label: "绑定 Agent", type: "select", value: defaultAssistantId, options: [{ value: "", label: "暂不绑定" }, ...assistants] }, { name: "language", label: "语言", type: "select", value: "auto", options: [{ value: "auto", label: "自动" }, { value: "zh-CN", label: "中文" }] } ], onSubmit: async (values) => { @@ -9210,15 +9228,17 @@ function openImportVideoLinkAction() { function openImportTextAction() { const project = requireSelectedProject(); const assistants = getAssistantOptions(project.id); + const defaultAssistantId = getSelectedAssistant()?.id || assistants[0]?.value || ""; openActionModal({ title: "导入文本素材", description: "把口播稿、拆解稿或灵感文本直接送进知识与分析链。", submitLabel: "开始分析", fields: [ + { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, - { name: "title", label: "标题", placeholder: "例如:创业口播拆解" }, + { name: "title", label: "标题", placeholder: "例如:本轮要分析的口播逐字稿" }, { name: "content", label: "正文", type: "textarea", rows: 8, placeholder: "粘贴需要分析的文本" }, - { name: "assistantId", label: "绑定 Agent", type: "select", value: assistants[0]?.value || "", options: [{ value: "", label: "暂不绑定" }, ...assistants] } + { name: "assistantId", label: "绑定 Agent", type: "select", value: defaultAssistantId, options: [{ value: "", label: "暂不绑定" }, ...assistants] } ], onSubmit: async (values) => { if (!values.title?.trim()) throw new Error("请填写标题"); @@ -9242,14 +9262,16 @@ function openImportTextAction() { function openUploadVideoAction() { const project = requireSelectedProject(); const assistants = getAssistantOptions(project.id); + const defaultAssistantId = getSelectedAssistant()?.id || assistants[0]?.value || ""; openActionModal({ title: "上传本地视频", description: "上传本地素材,直接进入分析链。", submitLabel: "上传并分析", fields: [ + { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, - { name: "title", label: "标题", placeholder: "可选,不填则用文件名" }, - { name: "assistantId", label: "绑定 Agent", type: "select", value: assistants[0]?.value || "", options: [{ value: "", label: "暂不绑定" }, ...assistants] }, + { name: "title", label: "标题", placeholder: "默认使用文件名,可选补一个更容易识别的标题" }, + { name: "assistantId", label: "绑定 Agent", type: "select", value: defaultAssistantId, options: [{ value: "", label: "暂不绑定" }, ...assistants] }, { name: "file", label: "本地视频", type: "file", accept: ".mp4,.mov,.m4v,.avi,.mkv,.webm" } ], onSubmit: async (values) => { diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index 88396c5..fe681d1 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -1135,6 +1135,25 @@ test("import and tracking sheets submit through direct execute handlers", () => assert.doesNotMatch(importText, /storyforgeFetch\("\/v2\/explore\/text"/); }); +test("input-heavy intake sheets surface current context and smarter defaults", () => { + const importVideo = extractBetween(APP, "function openImportVideoLinkAction()", "function openImportTextAction()"); + const importText = extractBetween(APP, "function openImportTextAction()", "function openUploadVideoAction()"); + const uploadVideo = extractBetween(APP, "function openUploadVideoAction()", "function focusPlaybookOneLinerWorkspace("); + assert.match(APP, /function renderIntakeActionContextHtml\(/); + assert.match(importVideo, /label: "当前上下文", type: "html"/); + assert.match(importVideo, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); + assert.match(importVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); + assert.match(importVideo, /placeholder: "例如:当前对标高分视频"/); + assert.match(importText, /label: "当前上下文", type: "html"/); + assert.match(importText, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); + assert.match(importText, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); + assert.match(importText, /placeholder: "例如:本轮要分析的口播逐字稿"/); + assert.match(uploadVideo, /label: "当前上下文", type: "html"/); + assert.match(uploadVideo, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); + assert.match(uploadVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); + assert.match(uploadVideo, /placeholder: "默认使用文件名,可选补一个更容易识别的标题"/); +}); + test("discovery analysis actions focus the most relevant detail tab after success", () => { const analyzeAccount = extractBetween(APP, "function openAnalyzeSelectedAccountAction()", "function openAnalyzeTopVideosAction()"); const analyzeTopVideos = extractBetween(APP, "function openAnalyzeTopVideosAction()", "function openSimilaritySearchAction()");