fix(ai): require closed stock photo vectors
- Reject open vector networks before contacting the provider\n- Cover closed and open vector targets explicitly\n- Label the stock photo prompt example code fence
This commit is contained in:
parent
90b608863e
commit
9a3d7ca702
|
|
@ -51,6 +51,13 @@ export async function applyPhoto(
|
|||
}
|
||||
}
|
||||
|
||||
if (node.type === 'VECTOR' && (node.vectorNetwork.regions?.length ?? 0) === 0) {
|
||||
return {
|
||||
id: req.id,
|
||||
error: `"${node.name}" has no closed vector regions — use closed area geometry`
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type !== 'BOOLEAN_OPERATION' && node.children.length > 0) {
|
||||
return { id: req.id, error: `"${node.name}" has children — use a leaf image placeholder` }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ No style={{}}, className, CSS. No named colors or rgb(). No percentage values. N
|
|||
|
||||
`stock_photo` places real Pexels images on leaf image placeholders. Prefer Rectangle, Rounded Rectangle, or Ellipse placeholders; closed vector and Boolean geometry are also supported. Pass a JSON array — **all photos fetched in parallel**:
|
||||
|
||||
```
|
||||
```text
|
||||
stock_photo({ requests: '[{"id":"0:30","query":"wall street trading floor"},{"id":"0:58","query":"AI chip semiconductor"},{"id":"0:65","query":"bank finance credit card"}]' })
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ const AREA_TARGET_TYPES = [
|
|||
'ELLIPSE',
|
||||
'STAR',
|
||||
'POLYGON',
|
||||
'VECTOR',
|
||||
'COMPONENT',
|
||||
'INSTANCE'
|
||||
] as const satisfies readonly NodeType[]
|
||||
|
|
@ -105,6 +104,69 @@ describe('applyPhoto', () => {
|
|||
})
|
||||
}
|
||||
|
||||
test('applies a photo to closed vector geometry', async () => {
|
||||
const { graph, page, figma } = setup()
|
||||
const vector = graph.createNode('VECTOR', page.id, {
|
||||
name: 'Closed vector',
|
||||
width: 100,
|
||||
height: 100,
|
||||
vectorNetwork: {
|
||||
vertices: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 100, y: 0 },
|
||||
{ x: 100, y: 100 },
|
||||
{ x: 0, y: 100 }
|
||||
],
|
||||
segments: [
|
||||
{ start: 0, end: 1, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
||||
{ start: 1, end: 2, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
||||
{ start: 2, end: 3, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
||||
{ start: 3, end: 0, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } }
|
||||
],
|
||||
regions: [{ windingRule: 'NONZERO', loops: [[0, 1, 2, 3]] }]
|
||||
}
|
||||
})
|
||||
const calls: ProviderCall[] = []
|
||||
|
||||
const result = await applyPhoto(figma, createProvider(calls), {
|
||||
id: vector.id,
|
||||
query: 'marble texture'
|
||||
})
|
||||
|
||||
expect(result.error).toBeUndefined()
|
||||
expect(calls).toHaveLength(1)
|
||||
expect(vector.fills[0]).toMatchObject({ type: 'IMAGE', imageScaleMode: 'FILL' })
|
||||
})
|
||||
|
||||
test('rejects open vector geometry before searching', async () => {
|
||||
const { graph, page, figma } = setup()
|
||||
const vector = graph.createNode('VECTOR', page.id, {
|
||||
name: 'Open vector',
|
||||
vectorNetwork: {
|
||||
vertices: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 100, y: 100 }
|
||||
],
|
||||
segments: [{ start: 0, end: 1, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } }],
|
||||
regions: []
|
||||
}
|
||||
})
|
||||
const originalFills = copyFills(vector.fills)
|
||||
const calls: ProviderCall[] = []
|
||||
|
||||
const result = await applyPhoto(figma, createProvider(calls), {
|
||||
id: vector.id,
|
||||
query: 'should not run'
|
||||
})
|
||||
|
||||
expect(result.error).toBe(
|
||||
`"${vector.name}" has no closed vector regions — use closed area geometry`
|
||||
)
|
||||
expect(calls).toHaveLength(0)
|
||||
expect(vector.fills).toEqual(originalFills)
|
||||
expect(graph.images.size).toBe(0)
|
||||
})
|
||||
|
||||
test('applies a photo to Boolean geometry with operand children', async () => {
|
||||
const { graph, page, figma } = setup()
|
||||
const operation = graph.createNode('BOOLEAN_OPERATION', page.id, {
|
||||
|
|
|
|||
Loading…
Reference in a new issue