feat: refresh live recorder sheet defaults
This commit is contained in:
@@ -11,6 +11,12 @@
|
||||
- `来源任务` 摘要区也会跟着联动更新,切换任务后第一眼就能看到当前承接的是哪条任务。
|
||||
- 为了支持这层联动,输入型表单里的 HTML 字段现在也带了稳定的 `data-action-field` 标记,后续继续做表单智能化和回归锁定会更稳。
|
||||
|
||||
### 直播录制表单开始跟随项目和平台动态刷新
|
||||
|
||||
- `新增录制源 / 编辑录制源` 现在会在切换项目或平台时动态刷新录制名称占位,并同步更新可选 Agent 列表,不再停留在打开表单时的默认值。
|
||||
- `导入 URL 配置` 现在会在切换平台时实时刷新说明文案和样例配置,抖音/快手两种场景可以直接在同一张表单里切换预设。
|
||||
- 这套联动同样保留“手动改过就不再覆盖”的原则,避免自动推荐把用户已经输入的内容冲掉。
|
||||
|
||||
## 2026-04-06
|
||||
|
||||
### 主 Agent 高注意图动作统一切到直执行入口
|
||||
|
||||
@@ -593,6 +593,67 @@ function bindCreativeSourceJobRecommendations(fields, options = {}) {
|
||||
sync();
|
||||
}
|
||||
|
||||
function bindLiveRecorderSheetRecommendations(fields, options = {}) {
|
||||
const projectSelect = fields.querySelector('[data-action-field="projectId"]');
|
||||
const platformSelect = fields.querySelector('[data-action-field="platform"]');
|
||||
const titleInput = fields.querySelector('[data-action-field="title"]');
|
||||
const assistantSelect = fields.querySelector('[data-action-field="assistantId"]');
|
||||
const rawTextarea = fields.querySelector('[data-action-field="raw"]');
|
||||
const defaultPlatform = options.defaultPlatform || "kuaishou";
|
||||
const syncAssistants = () => {
|
||||
if (!(assistantSelect instanceof HTMLSelectElement) || !(projectSelect instanceof HTMLSelectElement)) return;
|
||||
const projectId = projectSelect.value || "";
|
||||
const assistants = getAssistantOptions(projectId);
|
||||
const currentValue = assistantSelect.value || "";
|
||||
assistantSelect.innerHTML = [
|
||||
{ value: "", label: "暂不绑定" },
|
||||
...assistants
|
||||
].map((item) => `
|
||||
<option value="${escapeHtml(item.value)}" ${item.value === currentValue ? "selected" : ""}>${escapeHtml(item.label)}</option>
|
||||
`).join("");
|
||||
const stillExists = safeArray(assistants).some((item) => item.value === currentValue);
|
||||
if (!stillExists && currentValue) {
|
||||
assistantSelect.value = "";
|
||||
}
|
||||
};
|
||||
const syncTitle = () => {
|
||||
if (!(titleInput instanceof HTMLInputElement)) return;
|
||||
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 = recommendLiveRecorderTitle(project, platform);
|
||||
};
|
||||
const syncRawSamples = () => {
|
||||
if (!(rawTextarea instanceof HTMLTextAreaElement)) return;
|
||||
if (rawTextarea.dataset.recommendationMode === "manual") return;
|
||||
const platform = normalizePlatformValue(
|
||||
platformSelect instanceof HTMLSelectElement ? platformSelect.value : getPreferredPlatform(),
|
||||
defaultPlatform
|
||||
);
|
||||
rawTextarea.dataset.recommendationApplying = "1";
|
||||
rawTextarea.value = recommendLiveRecorderImportSamples(platform);
|
||||
rawTextarea.dataset.recommendationMode = "auto";
|
||||
delete rawTextarea.dataset.recommendationApplying;
|
||||
};
|
||||
rawTextarea?.addEventListener("input", () => {
|
||||
if (rawTextarea.dataset.recommendationApplying === "1") return;
|
||||
rawTextarea.dataset.recommendationMode = "manual";
|
||||
});
|
||||
const sync = () => {
|
||||
syncAssistants();
|
||||
syncTitle();
|
||||
syncRawSamples();
|
||||
};
|
||||
projectSelect?.addEventListener("change", sync);
|
||||
platformSelect?.addEventListener("change", sync);
|
||||
sync();
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return "-";
|
||||
const date = new Date(value);
|
||||
@@ -11887,6 +11948,9 @@ function openLiveRecorderCreateAction() {
|
||||
{ name: "sourceUrl", label: "直播源", type: "url", placeholder: "https://..." },
|
||||
{ name: "autoStart", label: "导入后立即开始", type: "checkbox", value: true }
|
||||
],
|
||||
onOpen: ({ fields }) => {
|
||||
bindLiveRecorderSheetRecommendations(fields, { defaultPlatform });
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
if (!values.sourceUrl?.trim()) throw new Error("请填写直播源链接");
|
||||
const saved = await storyforgeFetch("/v2/live-recorder/sources", {
|
||||
@@ -11956,6 +12020,9 @@ function openLiveRecorderSourceAction(sourceId) {
|
||||
{ name: "quality", label: "清晰度", type: "select", value: source.quality || "原画", options: ["原画", "蓝光", "超清", "高清", "标清", "流畅"].map((item) => ({ value: item, label: item })) },
|
||||
{ name: "enabled", label: "启用录制源", type: "checkbox", value: Boolean(source.enabled) }
|
||||
],
|
||||
onOpen: ({ fields }) => {
|
||||
bindLiveRecorderSheetRecommendations(fields, { defaultPlatform: source.platform || "kuaishou" });
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
const saved = await storyforgeFetch(`/v2/live-recorder/sources/${encodeURIComponent(source.id)}`, {
|
||||
method: "PATCH",
|
||||
@@ -11986,6 +12053,7 @@ function openLiveRecorderImportAction() {
|
||||
submitLabel: "导入并同步",
|
||||
fields: [
|
||||
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project?.id || "", defaultAssistantId) },
|
||||
{ name: "platform", label: "平台", type: "select", value: defaultPlatform, options: getPlatformOptions() },
|
||||
{
|
||||
name: "raw",
|
||||
label: "配置文本",
|
||||
@@ -11995,6 +12063,20 @@ function openLiveRecorderImportAction() {
|
||||
placeholder: "一行一个 URL,支持 # 注释和 逗号分隔的清晰度/标题"
|
||||
}
|
||||
],
|
||||
onOpen: ({ fields, description }) => {
|
||||
bindLiveRecorderSheetRecommendations(fields, { defaultPlatform });
|
||||
const platformSelect = fields.querySelector('[data-action-field="platform"]');
|
||||
const syncDescription = () => {
|
||||
if (!(description instanceof HTMLElement)) return;
|
||||
const platform = normalizePlatformValue(
|
||||
platformSelect instanceof HTMLSelectElement ? platformSelect.value : getPreferredPlatform(),
|
||||
defaultPlatform
|
||||
);
|
||||
description.textContent = `按行粘贴${platformLabel(platform)}直播源,支持用逗号附带清晰度和标题,注释行会被视为停用源。`;
|
||||
};
|
||||
platformSelect?.addEventListener("change", syncDescription);
|
||||
syncDescription();
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
if (!String(values.raw || "").trim()) throw new Error("请先粘贴配置文本");
|
||||
const saved = await storyforgeFetch("/v2/live-recorder/url-config/import", {
|
||||
|
||||
@@ -1197,6 +1197,7 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
|
||||
assert.match(APP, /function recommendManualIntakeTitle\(project, platform, kind\)/);
|
||||
assert.match(APP, /function bindManualIntakeTitleRecommendation\(fields, kind, options = \{\}\)/);
|
||||
assert.match(APP, /function bindCreativeSourceJobRecommendations\(fields, options = \{\}\)/);
|
||||
assert.match(APP, /function bindLiveRecorderSheetRecommendations\(fields, options = \{\}\)/);
|
||||
assert.match(importHomepage, /label: "当前上下文", type: "html"/);
|
||||
assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
|
||||
assert.match(importHomepage, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "douyin"\)/);
|
||||
@@ -1263,17 +1264,20 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
|
||||
assert.match(liveRecorderCreate, /const defaultTitle = recommendLiveRecorderTitle\(project, defaultPlatform\)/);
|
||||
assert.match(liveRecorderCreate, /renderIntakeActionContextHtml\(project\?\.id \|\| "", defaultAssistantId\)/);
|
||||
assert.match(liveRecorderCreate, /value: defaultTitle, placeholder: defaultTitle/);
|
||||
assert.match(liveRecorderCreate, /onOpen:\s*\(\{ fields \}\) => \{\s*bindLiveRecorderSheetRecommendations\(fields, \{/);
|
||||
assert.match(liveRecorderSource, /label: "当前上下文", type: "html"/);
|
||||
assert.match(liveRecorderSource, /const defaultAssistantId = source\.assistant_id \|\| getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
|
||||
assert.match(liveRecorderSource, /const titlePlaceholder = recommendLiveRecorderTitle\(currentProject, source\.platform \|\| "kuaishou"\)/);
|
||||
assert.match(liveRecorderSource, /renderIntakeActionContextHtml\(currentProject\?\.id \|\| source\.project_id \|\| "", defaultAssistantId\)/);
|
||||
assert.match(liveRecorderSource, /placeholder: titlePlaceholder/);
|
||||
assert.match(liveRecorderSource, /onOpen:\s*\(\{ fields \}\) => \{\s*bindLiveRecorderSheetRecommendations\(fields, \{/);
|
||||
assert.match(liveRecorderImport, /label: "当前上下文", type: "html"/);
|
||||
assert.match(liveRecorderImport, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
|
||||
assert.match(liveRecorderImport, /const defaultPlatform = normalizePlatformValue\(getPreferredPlatform\(\), "kuaishou"\)/);
|
||||
assert.match(liveRecorderImport, /const samples = recommendLiveRecorderImportSamples\(defaultPlatform\)/);
|
||||
assert.match(liveRecorderImport, /renderIntakeActionContextHtml\(project\?\.id \|\| "", defaultAssistantId\)/);
|
||||
assert.match(liveRecorderImport, /按行粘贴\$\{platformLabel\(defaultPlatform\)\}直播源/);
|
||||
assert.match(liveRecorderImport, /onOpen:\s*\(\{ fields, description \}\) => \{\s*bindLiveRecorderSheetRecommendations\(fields, \{/);
|
||||
assert.match(reviewAction, /label: "当前上下文", type: "html"/);
|
||||
assert.match(reviewAction, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
|
||||
assert.match(reviewAction, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
|
||||
|
||||
Reference in New Issue
Block a user