feat: simplify AI design for young creators
This commit is contained in:
1 parent
7552cf5952
commit
756638a28a
17 files changed
+3595
-297
No files matched your search
@@ -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 } },
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user