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()");