feat: refresh intake title hints on form changes
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-06 16:43:39 +08:00
parent 02231cfb6e
commit 3b4fdbc70a
3 changed files with 42 additions and 1 deletions

View File

@@ -10,7 +10,7 @@
- 这样主 Agent 结果卡、动作注册表和工作台高频按钮现在共用同一套直执行链,后续回跳与结果落点也更一致。 - 这样主 Agent 结果卡、动作注册表和工作台高频按钮现在共用同一套直执行链,后续回跳与结果落点也更一致。
- `analyze_account / analyze_top_videos` 现在也统一切到 `direct-*`,并且在缺少当前选中账号时会自动回退到旧表单,不会把用户卡死在“缺少上下文”的提示上。 - `analyze_account / analyze_top_videos` 现在也统一切到 `direct-*`,并且在缺少当前选中账号时会自动回退到旧表单,不会把用户卡死在“缺少上下文”的提示上。
- `direct-search-similar / direct-save-benchmark-link` 现在也会在缺少当前账号或相似候选时自动回退到旧表单,避免“查相似 / 存对标”入口因为上下文不完整直接报错。 - `direct-search-similar / direct-save-benchmark-link` 现在也会在缺少当前账号或相似候选时自动回退到旧表单,避免“查相似 / 存对标”入口因为上下文不完整直接报错。
- `导入主页 / 导入作品链接 / 导入文本 / 上传视频` 这批输入型表单现在会按当前项目和当前平台自动推荐标题占位,不再一直停留在通用示例文案,打开就更贴近当前工作流 - `导入主页 / 导入作品链接 / 导入文本 / 上传视频` 这批输入型表单现在会按当前项目和当前平台自动推荐标题占位,并且在表单里切换项目或平台时会同步更新,不再一直停留在通用示例文案。
### 依赖健康卡开始显示服务部署位置 ### 依赖健康卡开始显示服务部署位置

View File

@@ -479,6 +479,30 @@ function recommendManualIntakeTitle(project, platform, kind) {
return `${platformName}${suffix}`; return `${platformName}${suffix}`;
} }
function bindManualIntakeTitleRecommendation(fields, kind, options = {}) {
const titleInput = fields.querySelector('[data-action-field="title"]');
if (!(titleInput instanceof HTMLInputElement)) return;
const projectField = options.projectField || "projectId";
const platformField = options.platformField || "platform";
const defaultPlatform = options.defaultPlatform || "douyin";
const projectSelect = fields.querySelector(`[data-action-field="${projectField}"]`);
const platformSelect = fields.querySelector(`[data-action-field="${platformField}"]`);
const sync = () => {
const projectId = projectSelect instanceof HTMLSelectElement ? projectSelect.value : "";
const project = safeArray(appState.dashboard?.projects).find((item) => item.id === projectId)
|| getSelectedProject()
|| null;
const platform = normalizePlatformValue(
platformSelect instanceof HTMLSelectElement ? platformSelect.value : getPreferredPlatform(),
defaultPlatform
);
titleInput.placeholder = recommendManualIntakeTitle(project, platform, kind);
};
projectSelect?.addEventListener("change", sync);
platformSelect?.addEventListener("change", sync);
sync();
}
function formatDateTime(value) { function formatDateTime(value) {
if (!value) return "-"; if (!value) return "-";
const date = new Date(value); const date = new Date(value);
@@ -9270,6 +9294,9 @@ function openImportHomepageAction() {
title: "导入主页并同步", title: "导入主页并同步",
description: "适合抖音 / 小红书 / B站 / 快手 / 视频号主页。先建内容源,再触发同步与分析。", description: "适合抖音 / 小红书 / B站 / 快手 / 视频号主页。先建内容源,再触发同步与分析。",
submitLabel: "开始同步", submitLabel: "开始同步",
onOpen: ({ fields }) => {
bindManualIntakeTitleRecommendation(fields, "主页对标", { defaultPlatform: "douyin" });
},
fields: [ fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
{ name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() },
@@ -9399,6 +9426,9 @@ function openImportVideoLinkAction() {
title: "导入作品链接", title: "导入作品链接",
description: "直接把单条视频链接送进分析链。", description: "直接把单条视频链接送进分析链。",
submitLabel: "开始分析", submitLabel: "开始分析",
onOpen: ({ fields }) => {
bindManualIntakeTitleRecommendation(fields, "单条作品", { defaultPlatform: "douyin" });
},
fields: [ fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
{ name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() },
@@ -9436,6 +9466,9 @@ function openImportTextAction() {
title: "导入文本素材", title: "导入文本素材",
description: "把口播稿、拆解稿或灵感文本直接送进知识与分析链。", description: "把口播稿、拆解稿或灵感文本直接送进知识与分析链。",
submitLabel: "开始分析", submitLabel: "开始分析",
onOpen: ({ fields }) => {
bindManualIntakeTitleRecommendation(fields, "文本素材", { defaultPlatform: "douyin" });
},
fields: [ fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
{ name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() },
@@ -9472,6 +9505,9 @@ function openUploadVideoAction() {
title: "上传本地视频", title: "上传本地视频",
description: "上传本地素材,直接进入分析链。", description: "上传本地素材,直接进入分析链。",
submitLabel: "上传并分析", submitLabel: "上传并分析",
onOpen: ({ fields }) => {
bindManualIntakeTitleRecommendation(fields, "本地视频", { defaultPlatform: "douyin" });
},
fields: [ fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) }, { name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
{ name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() }, { name: "projectId", label: "归属项目", type: "select", value: project.id, options: getProjectOptions() },

View File

@@ -1195,6 +1195,7 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(APP, /function recommendLiveRecorderTitle\(project, platform\)/); assert.match(APP, /function recommendLiveRecorderTitle\(project, platform\)/);
assert.match(APP, /function recommendLiveRecorderImportSamples\(platform\)/); assert.match(APP, /function recommendLiveRecorderImportSamples\(platform\)/);
assert.match(APP, /function recommendManualIntakeTitle\(project, platform, kind\)/); assert.match(APP, /function recommendManualIntakeTitle\(project, platform, kind\)/);
assert.match(APP, /function bindManualIntakeTitleRecommendation\(fields, kind, options = \{\}\)/);
assert.match(importHomepage, /label: "当前上下文", type: "html"/); assert.match(importHomepage, /label: "当前上下文", type: "html"/);
assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(importHomepage, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/); assert.match(importHomepage, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/);
@@ -1202,6 +1203,7 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(importHomepage, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); assert.match(importHomepage, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(importHomepage, /value: defaultPlatform/); assert.match(importHomepage, /value: defaultPlatform/);
assert.match(importHomepage, /placeholder: titlePlaceholder/); assert.match(importHomepage, /placeholder: titlePlaceholder/);
assert.match(importHomepage, /onOpen:\s*\(\{ fields \}\) => \{\s*bindManualIntakeTitleRecommendation\(fields, "主页对标", \{ defaultPlatform: "douyin" \}\);/);
assert.match(importSelected, /label: "当前上下文", type: "html"/); assert.match(importSelected, /label: "当前上下文", type: "html"/);
assert.match(importSelected, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); assert.match(importSelected, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(importSelected, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); assert.match(importSelected, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
@@ -1214,18 +1216,21 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(importVideo, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "单条作品"\)/); assert.match(importVideo, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "单条作品"\)/);
assert.match(importVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); assert.match(importVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(importVideo, /placeholder: titlePlaceholder/); assert.match(importVideo, /placeholder: titlePlaceholder/);
assert.match(importVideo, /onOpen:\s*\(\{ fields \}\) => \{\s*bindManualIntakeTitleRecommendation\(fields, "单条作品", \{ defaultPlatform: "douyin" \}\);/);
assert.match(importText, /label: "当前上下文", type: "html"/); assert.match(importText, /label: "当前上下文", type: "html"/);
assert.match(importText, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); assert.match(importText, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(importText, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/); assert.match(importText, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/);
assert.match(importText, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "文本素材"\)/); assert.match(importText, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "文本素材"\)/);
assert.match(importText, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); assert.match(importText, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(importText, /placeholder: titlePlaceholder/); assert.match(importText, /placeholder: titlePlaceholder/);
assert.match(importText, /onOpen:\s*\(\{ fields \}\) => \{\s*bindManualIntakeTitleRecommendation\(fields, "文本素材", \{ defaultPlatform: "douyin" \}\);/);
assert.match(uploadVideo, /label: "当前上下文", type: "html"/); assert.match(uploadVideo, /label: "当前上下文", type: "html"/);
assert.match(uploadVideo, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/); assert.match(uploadVideo, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(uploadVideo, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/); assert.match(uploadVideo, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/);
assert.match(uploadVideo, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "本地视频"\)/); assert.match(uploadVideo, /const titlePlaceholder = recommendManualIntakeTitle\(project, defaultPlatform, "本地视频"\)/);
assert.match(uploadVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/); assert.match(uploadVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(uploadVideo, /placeholder: titlePlaceholder/); assert.match(uploadVideo, /placeholder: titlePlaceholder/);
assert.match(uploadVideo, /onOpen:\s*\(\{ fields \}\) => \{\s*bindManualIntakeTitleRecommendation\(fields, "本地视频", \{ defaultPlatform: "douyin" \}\);/);
assert.match(generateCopy, /label: "当前上下文", type: "html"/); assert.match(generateCopy, /label: "当前上下文", type: "html"/);
assert.match(generateCopy, /renderIntakeActionContextHtml\(project\?\.id \|\| "", assistant\.id\)/); assert.match(generateCopy, /renderIntakeActionContextHtml\(project\?\.id \|\| "", assistant\.id\)/);
assert.match(generateCopy, /sourceJob \? \[\{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml\(sourceJob\) \}\] : \[\]/); assert.match(generateCopy, /sourceJob \? \[\{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml\(sourceJob\) \}\] : \[\]/);