Commit dac2188a authored by edy's avatar edy

fix(ui): filter douyin task artifacts

parent 782eb235
Pipeline #18478 failed
...@@ -4,7 +4,7 @@ import { renderExpertIcon } from "../../components/icons/AppIcons" ...@@ -4,7 +4,7 @@ import { renderExpertIcon } from "../../components/icons/AppIcons"
import { Panel } from "../../components/ui/Panel" import { Panel } from "../../components/ui/Panel"
import { ScrollArea } from "../../components/ui/ScrollArea" import { ScrollArea } from "../../components/ui/ScrollArea"
import type { ExpertVisualKey } from "../shell/ExpertTree" import type { ExpertVisualKey } from "../shell/ExpertTree"
import { getDefaultTaskPanelDate, loadTaskPanelItems, summarizeTaskPanelItems } from "./taskPanelData" import { getDefaultTaskPanelDate, getVisibleTaskPanelArtifacts, loadTaskPanelItems, summarizeTaskPanelItems } from "./taskPanelData"
function resolveTaskExpertIconKey(expertName: string): ExpertVisualKey { function resolveTaskExpertIconKey(expertName: string): ExpertVisualKey {
const seed = expertName.toLowerCase() const seed = expertName.toLowerCase()
...@@ -324,7 +324,7 @@ export function TaskPanelView() { ...@@ -324,7 +324,7 @@ export function TaskPanelView() {
const displayDate = useMemo(() => selectedDate.replaceAll("-", "/"), [selectedDate]) const displayDate = useMemo(() => selectedDate.replaceAll("-", "/"), [selectedDate])
const displayMonth = useMemo(() => `${Number(selectedDate.slice(5, 7))}`, [selectedDate]) const displayMonth = useMemo(() => `${Number(selectedDate.slice(5, 7))}`, [selectedDate])
const outputItems = useMemo<TaskPanelOutputItem[]>(() => { const outputItems = useMemo<TaskPanelOutputItem[]>(() => {
return items.flatMap((task) => task.artifacts.map((artifact) => ({ artifact, task }))) return items.flatMap((task) => getVisibleTaskPanelArtifacts(task).map((artifact) => ({ artifact, task })))
}, [items]) }, [items])
return ( return (
......
import type { TaskPanelItem } from "@qjclaw/shared-types" import type { TaskPanelArtifact, TaskPanelItem } from "@qjclaw/shared-types"
export interface TaskPanelSummary { export interface TaskPanelSummary {
creditsUsed: number creditsUsed: number
...@@ -7,6 +7,27 @@ export interface TaskPanelSummary { ...@@ -7,6 +7,27 @@ export interface TaskPanelSummary {
employeeCount: number employeeCount: number
} }
const douyinVisibleArtifactExtensions = new Set([".docx", ".mp4"])
function isDouyinTaskPanelExpert(expertName: string) {
return /douyin|抖音/.test(expertName.toLowerCase())
}
function getTaskPanelArtifactExtension(artifact: TaskPanelArtifact) {
const source = (artifact.path ?? artifact.url ?? artifact.name).trim()
const cleanSource = source.split(/[?#]/, 1)[0] ?? ""
const match = cleanSource.match(/\.([a-z0-9]+)$/i)
return match ? `.${match[1].toLowerCase()}` : ""
}
export function getVisibleTaskPanelArtifacts(task: Pick<TaskPanelItem, "expertName" | "artifacts">) {
if (!isDouyinTaskPanelExpert(task.expertName)) {
return task.artifacts
}
return task.artifacts.filter((artifact) => douyinVisibleArtifactExtensions.has(getTaskPanelArtifactExtension(artifact)))
}
function toDateInputValue(date: Date) { function toDateInputValue(date: Date) {
const year = date.getFullYear() const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, "0") const month = String(date.getMonth() + 1).padStart(2, "0")
...@@ -57,6 +78,24 @@ export const mockTaskPanelItems: TaskPanelItem[] = [ ...@@ -57,6 +78,24 @@ export const mockTaskPanelItems: TaskPanelItem[] = [
{ id: "artifact-content-review", name: "昨日内容复盘与下轮优化建议.md", kind: "文档", summary: "互动、转化和评论反馈的复盘结论。", url: "/Users/edy/Documents/qianjiangclaw/tasks/content/昨日内容复盘与下轮优化建议.md" } { id: "artifact-content-review", name: "昨日内容复盘与下轮优化建议.md", kind: "文档", summary: "互动、转化和评论反馈的复盘结论。", url: "/Users/edy/Documents/qianjiangclaw/tasks/content/昨日内容复盘与下轮优化建议.md" }
] ]
}, },
{
id: "mock-task-douyin-delivery",
date: getDefaultTaskPanelDate(),
expertName: "抖音专家",
taskTitle: "生成短视频脚本与成片",
status: "completed",
statusDetail: "已完成脚本、成片和过程素材归档",
creditsUsed: 1480,
messageCount: 32,
updatedAt: "11:48",
artifacts: [
{ id: "artifact-douyin-script", name: "抖音口播脚本.docx", kind: "文档", summary: "数字人口播脚本、分镜节奏和字幕提示。", url: "/Users/edy/Documents/qianjiangclaw/tasks/douyin/抖音口播脚本.docx" },
{ id: "artifact-douyin-video", name: "抖音成片.mp4", kind: "视频", summary: "可发布的短视频成片。", url: "/Users/edy/Documents/qianjiangclaw/tasks/douyin/抖音成片.mp4" },
{ id: "artifact-douyin-notes", name: "执行过程记录.md", kind: "文档", summary: "运行日志和中间步骤记录。", url: "/Users/edy/Documents/qianjiangclaw/tasks/douyin/执行过程记录.md" },
{ id: "artifact-douyin-assets", name: "素材清单.xlsx", kind: "表格", summary: "镜头素材、来源和处理状态。", url: "/Users/edy/Documents/qianjiangclaw/tasks/douyin/素材清单.xlsx" },
{ id: "artifact-douyin-cover", name: "封面预览.png", kind: "图片", summary: "封面预览图。", url: "/Users/edy/Documents/qianjiangclaw/tasks/douyin/封面预览.png" }
]
},
{ {
id: "mock-task-zhihu", id: "mock-task-zhihu",
date: getDefaultTaskPanelDate(), date: getDefaultTaskPanelDate(),
...@@ -133,7 +172,7 @@ export function summarizeTaskPanelItems(items: TaskPanelItem[]): TaskPanelSummar ...@@ -133,7 +172,7 @@ export function summarizeTaskPanelItems(items: TaskPanelItem[]): TaskPanelSummar
const summary = items.reduce<TaskPanelSummary>((nextSummary, item) => { const summary = items.reduce<TaskPanelSummary>((nextSummary, item) => {
nextSummary.creditsUsed += item.creditsUsed ?? 0 nextSummary.creditsUsed += item.creditsUsed ?? 0
nextSummary.messageCount += item.messageCount ?? 0 nextSummary.messageCount += item.messageCount ?? 0
nextSummary.artifactCount += item.artifacts.length nextSummary.artifactCount += getVisibleTaskPanelArtifacts(item).length
return nextSummary return nextSummary
}, { }, {
creditsUsed: 0, creditsUsed: 0,
......
...@@ -71,3 +71,76 @@ test("summarizes task panel items from real artifacts and messages", async () => ...@@ -71,3 +71,76 @@ test("summarizes task panel items from real artifacts and messages", async () =>
employeeCount: 2 employeeCount: 2
}) })
}) })
test("filters task panel artifacts for Douyin experts only", async () => {
const { getVisibleTaskPanelArtifacts } = await import("../src/features/tasks/taskPanelData.ts")
const douyinArtifacts = [
{ id: "script", name: "口播脚本.docx", path: "/tmp/口播脚本.docx" },
{ id: "video", name: "成片.MP4", path: "/tmp/成片.MP4" },
{ id: "notes", name: "执行记录.md", path: "/tmp/执行记录.md" },
{ id: "sheet", name: "素材清单.xlsx", path: "/tmp/素材清单.xlsx" },
{ id: "cover", name: "封面.png", path: "/tmp/封面.png" }
]
const xhsArtifacts = [
{ id: "xhs-note", name: "小红书笔记.md", path: "/tmp/小红书笔记.md" },
{ id: "xhs-cover", name: "封面.png", path: "/tmp/封面.png" }
]
assert.deepEqual(
getVisibleTaskPanelArtifacts({ expertName: "抖音专家", artifacts: douyinArtifacts }),
[douyinArtifacts[0], douyinArtifacts[1]]
)
assert.deepEqual(
getVisibleTaskPanelArtifacts({ expertName: "openclaw-douyin-skills-delivery", artifacts: douyinArtifacts }),
[douyinArtifacts[0], douyinArtifacts[1]]
)
assert.deepEqual(
getVisibleTaskPanelArtifacts({ expertName: "小红书专家", artifacts: xhsArtifacts }),
xhsArtifacts
)
})
test("summarizes artifact count from visible task panel artifacts", async () => {
const { summarizeTaskPanelItems } = await import("../src/features/tasks/taskPanelData.ts")
assert.deepEqual(summarizeTaskPanelItems([
{
id: "douyin",
date: "2026-05-15",
expertName: "抖音专家",
taskTitle: "douyin",
status: "completed",
statusDetail: "已完成",
creditsUsed: 8,
messageCount: 3,
updatedAt: "10:00:00",
artifacts: [
{ id: "docx", name: "脚本.docx", path: "/tmp/脚本.docx" },
{ id: "mp4", name: "成片.mp4", path: "/tmp/成片.mp4" },
{ id: "md", name: "过程.md", path: "/tmp/过程.md" },
{ id: "png", name: "封面.png", path: "/tmp/封面.png" }
]
},
{
id: "xhs",
date: "2026-05-15",
expertName: "小红书专家",
taskTitle: "xhs",
status: "completed",
statusDetail: "已完成",
creditsUsed: 5,
messageCount: 2,
updatedAt: "11:00:00",
artifacts: [
{ id: "xhs-md", name: "笔记.md", path: "/tmp/笔记.md" },
{ id: "xhs-png", name: "封面.png", path: "/tmp/封面.png" }
]
}
]), {
creditsUsed: 13,
messageCount: 5,
artifactCount: 4,
employeeCount: 2
})
})
...@@ -33,6 +33,12 @@ test("task panel output copy feedback is scoped to the clicked output row", () = ...@@ -33,6 +33,12 @@ test("task panel output copy feedback is scoped to the clicked output row", () =
assert.doesNotMatch(source, /copiedArtifactId === artifact\.id/) assert.doesNotMatch(source, /copiedArtifactId === artifact\.id/)
}) })
test("task panel output list uses visible artifact filtering", () => {
assert.match(source, /getVisibleTaskPanelArtifacts/)
assert.match(source, /getVisibleTaskPanelArtifacts\(task\)\.map/)
assert.doesNotMatch(source, /task\.artifacts\.map\(\(artifact\) => \(\{ artifact, task \}\)\)/)
})
test("task panel output summaries use a single-line visual ellipsis", () => { test("task panel output summaries use a single-line visual ellipsis", () => {
const summaryBlock = cssBlock(taskStylesSource, ".task-panel-output-main p") const summaryBlock = cssBlock(taskStylesSource, ".task-panel-output-main p")
assert.match(summaryBlock, /overflow:\s*hidden;/) assert.match(summaryBlock, /overflow:\s*hidden;/)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment