修复画布任务列表完成展示
阻止画布生成完成快照自动展开生成器对话框 已完成任务按完成时间排序 恢复任务列表状态、时间和进度展示 (cherry picked from commit febe4811768b1bce73dbc37ca412b3834da2d699)
This commit is contained in:
@@ -3729,7 +3729,7 @@ fn apply_editor_canvas_generation_items(
|
||||
&& let Some(dialog) = item.get_mut("dialog").and_then(Value::as_object_mut)
|
||||
{
|
||||
dialog.insert("status".to_string(), json!("idle"));
|
||||
dialog.insert("composerOpen".to_string(), json!(true));
|
||||
dialog.insert("composerOpen".to_string(), json!(false));
|
||||
if let Some(completed_layer_id) = completed_layer_id.as_deref() {
|
||||
dialog.insert("generatedLayerId".to_string(), json!(completed_layer_id));
|
||||
}
|
||||
@@ -5482,7 +5482,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_canvas_generation_completion_inserts_result_layer_and_marks_dialog_idle() {
|
||||
fn editor_canvas_generation_completion_inserts_result_layer_and_keeps_composer_closed() {
|
||||
let layers = json!([
|
||||
{
|
||||
"layerId": "layer-existing",
|
||||
@@ -5585,7 +5585,7 @@ mod tests {
|
||||
.and_then(|item| item.get("dialog"))
|
||||
.expect("dialog should remain");
|
||||
assert_eq!(dialog["status"], json!("idle"));
|
||||
assert_eq!(dialog["composerOpen"], json!(true));
|
||||
assert_eq!(dialog["composerOpen"], json!(false));
|
||||
assert_eq!(
|
||||
dialog["generatedLayerId"],
|
||||
json!("layer-editor-resource-editor-resource-character")
|
||||
|
||||
@@ -592,10 +592,8 @@ fn list_external_generation_jobs_tx(
|
||||
external_generation_job_sort_bucket(left)
|
||||
.cmp(&external_generation_job_sort_bucket(right))
|
||||
.then_with(|| {
|
||||
right
|
||||
.updated_at
|
||||
.to_micros_since_unix_epoch()
|
||||
.cmp(&left.updated_at.to_micros_since_unix_epoch())
|
||||
external_generation_job_sort_time_micros(right)
|
||||
.cmp(&external_generation_job_sort_time_micros(left))
|
||||
})
|
||||
.then_with(|| left.job_id.cmp(&right.job_id))
|
||||
});
|
||||
@@ -970,6 +968,16 @@ fn external_generation_job_sort_bucket(row: &ExternalGenerationJob) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
fn external_generation_job_sort_time_micros(row: &ExternalGenerationJob) -> i64 {
|
||||
if is_external_generation_job_terminal(row) {
|
||||
return row
|
||||
.completed_at
|
||||
.unwrap_or(row.updated_at)
|
||||
.to_micros_since_unix_epoch();
|
||||
}
|
||||
row.updated_at.to_micros_since_unix_epoch()
|
||||
}
|
||||
|
||||
fn normalize_external_generation_job_status_filter(statuses: &[String]) -> Vec<&'static str> {
|
||||
statuses
|
||||
.iter()
|
||||
@@ -1252,6 +1260,23 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_list_sort_time_uses_completion_time_for_terminal_jobs() {
|
||||
let mut completed = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED);
|
||||
completed.completed_at = Some(micros(2_000));
|
||||
completed.updated_at = micros(9_000);
|
||||
let mut older_completed =
|
||||
external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED);
|
||||
older_completed.completed_at = Some(micros(1_000));
|
||||
older_completed.updated_at = micros(10_000);
|
||||
|
||||
assert!(external_generation_job_sort_time_micros(&completed) > 1_000);
|
||||
assert!(
|
||||
external_generation_job_sort_time_micros(&completed)
|
||||
> external_generation_job_sort_time_micros(&older_completed)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_list_status_filter_accepts_public_queued_alias() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -90,6 +90,15 @@ describe('ImageCanvasTaskSidebarView', () => {
|
||||
}),
|
||||
]
|
||||
: [
|
||||
createExternalTask({
|
||||
jobId: 'older-done-task',
|
||||
requestLabel: '较早完成',
|
||||
requestPrompt: '较早完成任务',
|
||||
status: 'completed',
|
||||
startedAt: new Date(Date.now() - 9000).toISOString(),
|
||||
completedAt: new Date(Date.now() - 8000).toISOString(),
|
||||
updatedAt: new Date(Date.now() - 500).toISOString(),
|
||||
}),
|
||||
createExternalTask({
|
||||
jobId: 'done-task',
|
||||
requestLabel: '视频生成',
|
||||
@@ -151,8 +160,14 @@ describe('ImageCanvasTaskSidebarView', () => {
|
||||
const completedTab = screen.getByRole('tab', { name: /已完成/u });
|
||||
fireEvent.click(completedTab);
|
||||
expect(await screen.findByText('视频生成')).toBeTruthy();
|
||||
const completedTitles = screen
|
||||
.getAllByRole('button')
|
||||
.map((button) => button.textContent ?? '')
|
||||
.filter((text) => text.includes('视频生成') || text.includes('较早完成'));
|
||||
expect(completedTitles[0]).toContain('视频生成');
|
||||
expect(completedTitles[1]).toContain('较早完成');
|
||||
expect(screen.getByText(/生成一段开场动画/u)).toBeTruthy();
|
||||
expect(screen.queryByText(/完成于/u)).not.toBeNull();
|
||||
expect(screen.queryAllByText(/完成于/u).length).toBeGreaterThan(0);
|
||||
expect(screen.queryByText(/生成完成/u)).toBeNull();
|
||||
|
||||
fireEvent.wheel(screen.getByLabelText('画布任务列表'), { deltaY: 120 });
|
||||
@@ -220,9 +235,11 @@ describe('ImageCanvasTaskSidebarView', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText('生成图片')).toBeTruthy();
|
||||
expect(screen.getByText(/总进度 35% · 正在生成第 2\/4 段。/u)).toBeTruthy();
|
||||
expect(await screen.findByText('图片画布生成图片')).toBeTruthy();
|
||||
expect(screen.getByText(/发光猫咪主视觉/u)).toBeTruthy();
|
||||
expect(screen.getByText(/35% · 正在生成第 2\/4 段。/u)).toBeTruthy();
|
||||
expect(screen.getByText(/已用时 1分/u)).toBeTruthy();
|
||||
expect(screen.queryByText(/总进度/u)).toBeNull();
|
||||
expect(screen.queryByText(/不该显示的外部项目任务/u)).toBeNull();
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
|
||||
@@ -101,8 +101,13 @@ function externalTaskDetail(
|
||||
);
|
||||
}
|
||||
const phaseLabel = task.phaseLabel.trim();
|
||||
const phaseDetail = task.phaseDetail.trim();
|
||||
return phaseLabel || phaseDetail || task.requestLabel.trim() || task.jobKind;
|
||||
const requestLabel = task.requestLabel.trim();
|
||||
const prompt = normalizeTaskPrompt(task.requestPrompt);
|
||||
return (
|
||||
(prompt && prompt !== requestLabel ? prompt : null) ||
|
||||
(phaseLabel && phaseLabel !== requestLabel ? phaseLabel : null) ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
function externalTaskProgressDetail(
|
||||
@@ -114,7 +119,7 @@ function externalTaskProgressDetail(
|
||||
}
|
||||
const values = [
|
||||
Number.isFinite(task.progress) && task.progress > 0
|
||||
? `总进度 ${Math.min(100, Math.max(0, Math.round(task.progress)))}%`
|
||||
? `${Math.min(100, Math.max(0, Math.round(task.progress)))}%`
|
||||
: null,
|
||||
task.phaseDetail.trim(),
|
||||
].filter((value): value is string => Boolean(value));
|
||||
@@ -445,23 +450,7 @@ export function ImageCanvasTaskSidebarView({
|
||||
}, [externalTasks]);
|
||||
|
||||
const items = useMemo<TaskSidebarItem[]>(() => {
|
||||
const externalVisibleTasks = mergeExternalTasks(
|
||||
externalTasks.filter(
|
||||
(task) => task.status === 'queued' || task.status === 'running',
|
||||
),
|
||||
externalTasks
|
||||
.filter(isTerminalExternalTask)
|
||||
.sort(
|
||||
(left, right) =>
|
||||
(parseTaskTimeMs(right.completedAt) ??
|
||||
parseTaskTimeMs(right.updatedAt) ??
|
||||
0) -
|
||||
(parseTaskTimeMs(left.completedAt) ??
|
||||
parseTaskTimeMs(left.updatedAt) ??
|
||||
0),
|
||||
)
|
||||
.slice(0, completedListLimit),
|
||||
);
|
||||
const externalVisibleTasks = externalTasks;
|
||||
const externalItems = externalVisibleTasks.map<TaskSidebarItem>((task) => {
|
||||
const status = canvasTaskStatusFromExternalTask(task.status);
|
||||
const startedAt =
|
||||
@@ -493,7 +482,7 @@ export function ImageCanvasTaskSidebarView({
|
||||
};
|
||||
});
|
||||
return externalItems;
|
||||
}, [completedListLimit, externalTasks, now, onFocusExternalTask]);
|
||||
}, [externalTasks, now, onFocusExternalTask]);
|
||||
|
||||
const activeItems = useMemo(
|
||||
() =>
|
||||
@@ -635,6 +624,7 @@ export function ImageCanvasTaskSidebarView({
|
||||
const titleText = [detailText, item.progressDetail, timeText]
|
||||
.filter((value): value is string => Boolean(value))
|
||||
.join(' · ');
|
||||
const metaTimeText = timeText;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
@@ -651,8 +641,15 @@ export function ImageCanvasTaskSidebarView({
|
||||
<span className="image-canvas-editor__task-sidebar-item-title">
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="image-canvas-editor__task-sidebar-item-status">
|
||||
{statusLabel(item.status)}
|
||||
<span className="image-canvas-editor__task-sidebar-item-meta">
|
||||
<span className="image-canvas-editor__task-sidebar-item-status">
|
||||
{statusLabel(item.status)}
|
||||
</span>
|
||||
{metaTimeText ? (
|
||||
<span className="image-canvas-editor__task-sidebar-item-time">
|
||||
{metaTimeText}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
@@ -669,11 +666,6 @@ export function ImageCanvasTaskSidebarView({
|
||||
{item.progressDetail}
|
||||
</span>
|
||||
) : null}
|
||||
{timeText ? (
|
||||
<span className="image-canvas-editor__task-sidebar-item-time">
|
||||
{timeText}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
+9
-1
@@ -6445,7 +6445,7 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
align-items: start;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
@@ -6485,6 +6485,14 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.image-canvas-editor__task-sidebar-item-meta {
|
||||
display: inline-flex;
|
||||
min-width: max-content;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 0.36rem;
|
||||
}
|
||||
|
||||
.image-canvas-editor__task-sidebar-item-status {
|
||||
color: #64748b;
|
||||
font-size: 0.68rem;
|
||||
|
||||
Reference in New Issue
Block a user