fix: resolve confirmed character hidden bug, remove online font dependency, improve UI/UX experience

This commit is contained in:
saturn
2026-03-21 14:35:32 +08:00
parent f364bbc9e4
commit a6ad11b9c4
42 changed files with 1189 additions and 553 deletions

View File

@@ -0,0 +1,80 @@
import * as React from 'react'
import { createElement } from 'react'
import type { ComponentProps, ReactElement } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { renderToStaticMarkup } from 'react-dom/server'
import { NextIntlClientProvider } from 'next-intl'
import type { AbstractIntlMessages } from 'next-intl'
import AssetToolbar from '@/app/[locale]/workspace/[projectId]/modes/novel-promotion/components/assets/AssetToolbar'
vi.mock('@/lib/query/hooks', () => ({
useProjectAssets: vi.fn(() => ({ data: { characters: [], locations: [], props: [] } })),
useProjectData: vi.fn(() => ({ data: { name: '项目A' } })),
}))
const messages = {
assets: {
common: {
refresh: '刷新',
},
filterBar: {
allEpisodes: '全部集数',
},
toolbar: {
assetManagement: '资产管理',
assetCount: '共 {total} 个资产({appearances} 角色形象 + {locations} 场景 + {props} 道具)',
globalAnalyze: '全局分析',
globalAnalyzeHint: '分析所有资产',
downloadAll: '下载全部',
generateAll: '生成全部图片',
regenerateAll: '重新生成全部',
regenerateAllHint: '重新生成所有图片',
},
assetLibrary: {
downloadEmpty: '没有可下载图片',
downloadFailed: '下载失败',
},
},
} as const
const renderWithIntl = (node: ReactElement) => {
const providerProps: ComponentProps<typeof NextIntlClientProvider> = {
locale: 'zh',
messages: messages as unknown as AbstractIntlMessages,
timeZone: 'Asia/Shanghai',
children: node,
}
return renderToStaticMarkup(
createElement(NextIntlClientProvider, providerProps),
)
}
describe('AssetToolbar', () => {
it('删除批量生成与刷新按钮 -> 仅保留全局分析和下载入口', () => {
Reflect.set(globalThis, 'React', React)
const html = renderWithIntl(
createElement(AssetToolbar, {
projectId: 'project-1',
totalAssets: 24,
totalAppearances: 11,
totalLocations: 13,
totalProps: 0,
isBatchSubmitting: false,
isAnalyzingAssets: false,
isGlobalAnalyzing: false,
onGlobalAnalyze: () => undefined,
episodeId: null,
onEpisodeChange: () => undefined,
episodes: [],
}),
)
expect(html).toContain('全局分析')
expect(html).toContain('title="下载全部"')
expect(html).not.toContain('生成全部图片')
expect(html).not.toContain('重新生成全部')
expect(html).not.toContain('>刷新<')
})
})

View File

@@ -24,6 +24,9 @@ const messages = {
waitingModelOutput: '等待模型输出...',
reasoningNotProvided: '该步骤未返回思考过程',
},
streamStep: {
analyzeProps: '道具分析',
},
runtime: {
llm: {
processing: '模型处理中...',
@@ -69,4 +72,27 @@ describe('LLMStageStreamCard error rendering', () => {
expect(html).not.toContain('Copy error detail')
expect(html).not.toContain('Open feedback form')
})
it('resolves analyze props progress keys without missing message errors', () => {
Reflect.set(globalThis, 'React', React)
const html = renderWithIntl(
createElement(LLMStageStreamCard, {
title: 'progress.streamStep.analyzeProps',
stages: [{
id: 'analyze_props',
title: 'progress.streamStep.analyzeProps',
subtitle: 'progress.streamStep.analyzeProps',
status: 'processing',
progress: 35,
}],
activeStageId: 'analyze_props',
activeMessage: 'progress.streamStep.analyzeProps',
outputText: '',
}),
)
expect(html).toContain('道具分析')
expect(html).not.toContain('progress.streamStep.analyzeProps')
expect(html).not.toContain('MISSING_MESSAGE')
})
})

View File

@@ -0,0 +1,29 @@
import * as React from 'react'
import { createElement } from 'react'
import { describe, expect, it } from 'vitest'
import { renderToStaticMarkup } from 'react-dom/server'
import { SegmentedControl } from '@/components/ui/SegmentedControl'
describe('SegmentedControl', () => {
it('compact 布局 -> 输出左对齐的非拉伸结构', () => {
Reflect.set(globalThis, 'React', React)
const html = renderToStaticMarkup(
createElement(SegmentedControl, {
options: [
{ value: 'all', label: '全部 (24)' },
{ value: 'character', label: '角色 (11)' },
{ value: 'location', label: '场景 (13)' },
{ value: 'prop', label: '道具 (0)' },
],
value: 'all',
onChange: () => undefined,
layout: 'compact',
}),
)
expect(html).toContain('inline-block max-w-full')
expect(html).toContain('inline-grid grid-flow-col auto-cols-[minmax(96px,max-content)]')
expect(html).not.toContain('grid-template-columns:repeat(4,minmax(0,1fr))')
})
})