feat: simplify AI design for young creators

This commit is contained in:
brother7 committed 2026-09-02 19:14:33 +08:00
1 parent 7552cf5952
commit 756638a28a
17 files changed
+3595 -297

No files matched your search

+120
View File
@@ -134,6 +134,117 @@ describe('V2 Living Form store', () => {
expect(useImageWorkspaceStore.getState().pendingOperations).toEqual({});
});
it('expires quote blockers only after the canonical Specification changes', async () => {
await loadedStore();
const blocker = {
code: 'aspect_ratio_required',
message: 'Choose an aspect ratio supported by the frozen route.',
severity: 'blocker' as const,
path: 'output.aspect_ratio',
};
useImageWorkspaceStore.setState({ quoteBlockers: [blocker] });
await useImageWorkspaceStore.getState().refreshWorkspace();
expect(useImageWorkspaceStore.getState().quoteBlockers).toEqual([blocker]);
const revisedWorkspace = designWorkspaceFixture({
form: designFormFixture({
directionRevision: 5,
specificationRevision: 4,
specificationRevisionId: 'specification-revision-4',
}),
});
submitCommandMock.mockResolvedValueOnce({
clientOperationId: 'operation-1',
runId: 'run-2',
workspace: revisedWorkspace,
});
await useImageWorkspaceStore.getState().applyFieldOperations([
{ kind: 'set', path: 'output.aspect_ratio', value: '16:9' },
]);
expect(useImageWorkspaceStore.getState().quoteBlockers).toEqual([]);
expect(useImageWorkspaceStore.getState().workspace?.form.specificationRevision).toBe(4);
});
it('treats a terminal quote-blocked response as definitive instead of unknown', async () => {
await loadedStore();
submitCommandMock.mockRejectedValueOnce(new ImageWorkspaceApiError(
422,
'design_quote_blocked',
'The frozen compiler needs more information.',
));
await expect(useImageWorkspaceStore.getState().requestQuote()).rejects.toMatchObject({
status: 422,
code: 'design_quote_blocked',
});
expect(useImageWorkspaceStore.getState().pendingOperations).toEqual({});
expect(useImageWorkspaceStore.getState().error).toBeNull();
});
it('settles an unknown quote from its blocked event, then allows a revised Quote', async () => {
const source = await loadedStore();
submitCommandMock.mockRejectedValueOnce(new Error('network disconnected'));
await expect(useImageWorkspaceStore.getState().requestQuote()).rejects.toThrow(
'network disconnected',
);
expect(useImageWorkspaceStore.getState().pendingOperations['operation-1']?.status).toBe('unknown');
const blocker = {
code: 'aspect_ratio_required',
message: 'Choose an aspect ratio supported by the frozen route.',
severity: 'blocker' as const,
path: 'output.aspect_ratio',
};
source.emit('design.quote.blocked', {
id: 'session-1:quote-blocked',
type: 'design.quote.blocked',
clientOperationId: 'operation-1',
blockers: [blocker],
warnings: [],
form: designFormFixture(),
});
expect(useImageWorkspaceStore.getState()).toMatchObject({
pendingOperations: {},
quoteBlockers: [blocker],
error: null,
});
const revisedWorkspace = designWorkspaceFixture({
form: designFormFixture({
directionRevision: 5,
specificationRevision: 4,
specificationRevisionId: 'specification-revision-4',
}),
});
submitCommandMock.mockResolvedValueOnce({
clientOperationId: 'operation-1',
runId: 'run-edit',
workspace: revisedWorkspace,
});
await useImageWorkspaceStore.getState().applyFieldOperations([
{ kind: 'set', path: 'output.aspect_ratio', value: '16:9' },
]);
expect(useImageWorkspaceStore.getState().quoteBlockers).toEqual([]);
submitCommandMock.mockResolvedValueOnce({
clientOperationId: 'operation-1',
runId: 'run-quote-revised',
workspace: revisedWorkspace,
});
await useImageWorkspaceStore.getState().requestQuote();
expect(submitCommandMock).toHaveBeenLastCalledWith(expect.objectContaining({
kind: 'request_quote',
expectedDirectionRevision: 5,
specificationRevision: 4,
}));
expect(useImageWorkspaceStore.getState().pendingOperations).toEqual({});
});
it('refreshes on a definitive revision conflict and preserves the unsent draft', async () => {
await loadedStore();
useImageWorkspaceStore.getState().setFieldDraft('intent.purpose', '新的用途');
@@ -187,6 +298,14 @@ describe('V2 Living Form store', () => {
it('reconciles assistant deltas and canonical Form events without mutating drafts', async () => {
const source = await loadedStore();
useImageWorkspaceStore.getState().setFieldDraft('intent.purpose', '本地草稿');
useImageWorkspaceStore.setState({
quoteBlockers: [{
code: 'missing_choice',
message: '还需要选择作品形状',
severity: 'blocker',
path: 'output.aspect_ratio',
}],
});
source.emit('design.assistant.delta', {
id: 'session-1:7',
type: 'design.assistant.delta',
@@ -234,6 +353,7 @@ describe('V2 Living Form store', () => {
lastEventId: 'session-1:8',
assistantStreams: { 'operation-chat-1': '正在整理' },
fieldDrafts: { 'intent.purpose': '本地草稿' },
quoteBlockers: [],
workspace: { form: { directionRevision: 5, specificationRevision: 4 } },
});
});