Merge branch 'native-authoring' into jsx-component-properties
# Conflicts: # packages/core/src/design-jsx/reference/authoring.md # packages/docs/reference/design-authoring.md # skills/open-pencil/references/design-authoring.md
This commit is contained in:
commit
b4f119a01b
|
|
@ -26,8 +26,8 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
## Variables and components
|
||||
|
||||
- Create document variables before referencing them with `designVar('id-or-name')`. `defineVars` groups references; it does not create variable collections.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`; grid gaps and wrapped `rowGap` also support them.
|
||||
- References preserve real graph bindings, not just copied values. Initial scalar layout resolves the parent's inherited collection mode. Missing or incorrectly typed scalar variables are errors.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`. Grid `columnGap`/`rowGap` and wrapped flex `rowGap` also support FLOAT references; grid `gap` overrides both axis-specific gaps. Use numbers or FLOAT references for these scalar props, not CSS unit strings.
|
||||
- References preserve real graph bindings, not just copied values. Set the intended collection mode on the parent before creating scalar-bound content: initial scalar layout resolves that inherited mode. This does not guarantee automatic scalar layout recomputation after a later mode switch. Verify resulting geometry as well as paint when changing modes. Missing or incorrectly typed scalar variables are errors.
|
||||
- `bind` maps supported scene-field paths to variable IDs or references when no shorthand exists. Use semantic tokens consistently rather than declaring unused collections.
|
||||
- A reusable JavaScript function shares source code, not component identity. Use `Component`, `ComponentSet`, and `Instance` for editable main components and linked instances.
|
||||
- `Instance` resolves an existing component through `of`, `component`, or `componentId`. Component-set children named `variant=Primary`, for example, define variants that can be selected when instantiating the set.
|
||||
|
|
@ -35,6 +35,7 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
- Child `propertyRefs` connect fields to stable property IDs, for example `[{ propertyId: 'message', field: 'TEXT' }]`. Supported fields are `TEXT`, `VISIBLE`, and `INSTANCE_SWAP`; text and swap references require text and instance nodes respectively. References do not depend on layer names.
|
||||
- Instance assignments use the native string values (including `'true'` / `'false'` for BOOLEAN properties and component IDs for swaps). For example `Instance({ of: noteId, properties: { message: 'Updated review' } })`. Assignments persist through component synchronization; unknown IDs and invalid values fail rather than silently creating inert overrides. Select variants through component-set variant props, not through instance property assignments.
|
||||
- Reuse existing local or library components before recreating them. Keep meaningful text, visibility, and swap properties exposed rather than hand-editing cloned child nodes.
|
||||
- Distinguish a main component's default size from its instance's placement constraints. Verify the actual instance bounds in narrower parents; a requested Fill dimension alone is not proof that inherited sizing changed. Do not compensate for a sizing mismatch with guessed heights, clipping, or manually positioned siblings.
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const AUTHORING_EXAMPLES: readonly AuthoringExample[] = [
|
|||
{
|
||||
title: 'Variable-bound spacing and typography',
|
||||
jsx: dedent`<Frame name="Bound note" w={280} h="hug" flex="col" gap={designVar('Space/small')} p={designVar('Space/medium')} bg="#FFFFFF">
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} color="#252A31">A note that grows with its content.</Text>
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} lineHeight={designVar('Type/body-leading')} letterSpacing={designVar('Type/body-tracking')} color="#252A31">A note that grows with its content.</Text>
|
||||
</Frame>`
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
## Variables and components
|
||||
|
||||
- Create document variables before referencing them with `designVar('id-or-name')`. `defineVars` groups references; it does not create variable collections.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`; grid gaps and wrapped `rowGap` also support them.
|
||||
- References preserve real graph bindings, not just copied values. Initial scalar layout resolves the parent's inherited collection mode. Missing or incorrectly typed scalar variables are errors.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`. Grid `columnGap`/`rowGap` and wrapped flex `rowGap` also support FLOAT references; grid `gap` overrides both axis-specific gaps. Use numbers or FLOAT references for these scalar props, not CSS unit strings.
|
||||
- References preserve real graph bindings, not just copied values. Set the intended collection mode on the parent before creating scalar-bound content: initial scalar layout resolves that inherited mode. This does not guarantee automatic scalar layout recomputation after a later mode switch. Verify resulting geometry as well as paint when changing modes. Missing or incorrectly typed scalar variables are errors.
|
||||
- `bind` maps supported scene-field paths to variable IDs or references when no shorthand exists. Use semantic tokens consistently rather than declaring unused collections.
|
||||
- A reusable JavaScript function shares source code, not component identity. Use `Component`, `ComponentSet`, and `Instance` for editable main components and linked instances.
|
||||
- `Instance` resolves an existing component through `of`, `component`, or `componentId`. Component-set children named `variant=Primary`, for example, define variants that can be selected when instantiating the set.
|
||||
|
|
@ -37,6 +37,7 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
- Child `propertyRefs` connect fields to stable property IDs, for example `[{ propertyId: 'message', field: 'TEXT' }]`. Supported fields are `TEXT`, `VISIBLE`, and `INSTANCE_SWAP`; text and swap references require text and instance nodes respectively. References do not depend on layer names.
|
||||
- Instance assignments use the native string values (including `'true'` / `'false'` for BOOLEAN properties and component IDs for swaps). For example `Instance({ of: noteId, properties: { message: 'Updated review' } })`. Assignments persist through component synchronization; unknown IDs and invalid values fail rather than silently creating inert overrides. Select variants through component-set variant props, not through instance property assignments.
|
||||
- Reuse existing local or library components before recreating them. Keep meaningful text, visibility, and swap properties exposed rather than hand-editing cloned child nodes.
|
||||
- Distinguish a main component's default size from its instance's placement constraints. Verify the actual instance bounds in narrower parents; a requested Fill dimension alone is not proof that inherited sizing changed. Do not compensate for a sizing mismatch with guessed heights, clipping, or manually positioned siblings.
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ The examples below are executed by the authoring-reference tests. Create the nam
|
|||
|
||||
```jsx
|
||||
<Frame name="Bound note" w={280} h="hug" flex="col" gap={designVar('Space/small')} p={designVar('Space/medium')} bg="#FFFFFF">
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} color="#252A31">A note that grows with its content.</Text>
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} lineHeight={designVar('Type/body-leading')} letterSpacing={designVar('Type/body-tracking')} color="#252A31">A note that grows with its content.</Text>
|
||||
</Frame>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
## Variables and components
|
||||
|
||||
- Create document variables before referencing them with `designVar('id-or-name')`. `defineVars` groups references; it does not create variable collections.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`; grid gaps and wrapped `rowGap` also support them.
|
||||
- References preserve real graph bindings, not just copied values. Initial scalar layout resolves the parent's inherited collection mode. Missing or incorrectly typed scalar variables are errors.
|
||||
- COLOR references work in paint props. FLOAT references work in `w`, `h`, `gap`, padding, corner radii, `strokeWidth`, `opacity`, text `size`/`fontSize`, `lineHeight`, and `letterSpacing`. Grid `columnGap`/`rowGap` and wrapped flex `rowGap` also support FLOAT references; grid `gap` overrides both axis-specific gaps. Use numbers or FLOAT references for these scalar props, not CSS unit strings.
|
||||
- References preserve real graph bindings, not just copied values. Set the intended collection mode on the parent before creating scalar-bound content: initial scalar layout resolves that inherited mode. This does not guarantee automatic scalar layout recomputation after a later mode switch. Verify resulting geometry as well as paint when changing modes. Missing or incorrectly typed scalar variables are errors.
|
||||
- `bind` maps supported scene-field paths to variable IDs or references when no shorthand exists. Use semantic tokens consistently rather than declaring unused collections.
|
||||
- A reusable JavaScript function shares source code, not component identity. Use `Component`, `ComponentSet`, and `Instance` for editable main components and linked instances.
|
||||
- `Instance` resolves an existing component through `of`, `component`, or `componentId`. Component-set children named `variant=Primary`, for example, define variants that can be selected when instantiating the set.
|
||||
|
|
@ -37,6 +37,7 @@ This reference describes scene creation, not React DOM output. Use the `render`
|
|||
- Child `propertyRefs` connect fields to stable property IDs, for example `[{ propertyId: 'message', field: 'TEXT' }]`. Supported fields are `TEXT`, `VISIBLE`, and `INSTANCE_SWAP`; text and swap references require text and instance nodes respectively. References do not depend on layer names.
|
||||
- Instance assignments use the native string values (including `'true'` / `'false'` for BOOLEAN properties and component IDs for swaps). For example `Instance({ of: noteId, properties: { message: 'Updated review' } })`. Assignments persist through component synchronization; unknown IDs and invalid values fail rather than silently creating inert overrides. Select variants through component-set variant props, not through instance property assignments.
|
||||
- Reuse existing local or library components before recreating them. Keep meaningful text, visibility, and swap properties exposed rather than hand-editing cloned child nodes.
|
||||
- Distinguish a main component's default size from its instance's placement constraints. Verify the actual instance bounds in narrower parents; a requested Fill dimension alone is not proof that inherited sizing changed. Do not compensate for a sizing mismatch with guessed heights, clipping, or manually positioned siblings.
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ The examples below are executed by the authoring-reference tests. Create the nam
|
|||
|
||||
```jsx
|
||||
<Frame name="Bound note" w={280} h="hug" flex="col" gap={designVar('Space/small')} p={designVar('Space/medium')} bg="#FFFFFF">
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} color="#252A31">A note that grows with its content.</Text>
|
||||
<Text name="Message" w="fill" size={designVar('Type/body')} lineHeight={designVar('Type/body-leading')} letterSpacing={designVar('Type/body-tracking')} color="#252A31">A note that grows with its content.</Text>
|
||||
</Frame>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -12,31 +12,41 @@ import SYSTEM_PROMPT from '@/app/ai/chat/system-prompt'
|
|||
import { getNodeOrThrow } from '#tests/helpers/assert'
|
||||
import { makeSceneGraph } from '#tests/helpers/scene'
|
||||
|
||||
function exampleGraph() {
|
||||
const graph = makeSceneGraph()
|
||||
graph.addCollection({
|
||||
id: 'tokens',
|
||||
name: 'Tokens',
|
||||
modes: [
|
||||
{ modeId: 'default', name: 'Default' },
|
||||
{ modeId: 'compact', name: 'Compact' }
|
||||
],
|
||||
defaultModeId: 'default',
|
||||
variableIds: []
|
||||
})
|
||||
for (const [name, value, compact] of [
|
||||
['Space/small', 8, 4],
|
||||
['Space/medium', 16, 12],
|
||||
['Type/body', 12, 11],
|
||||
['Type/body-leading', 20, 16],
|
||||
['Type/body-tracking', 0.2, 0]
|
||||
] as const) {
|
||||
graph.addVariable({
|
||||
id: name,
|
||||
name,
|
||||
type: 'FLOAT',
|
||||
collectionId: 'tokens',
|
||||
valuesByMode: { default: value, compact },
|
||||
description: '',
|
||||
hiddenFromPublishing: false
|
||||
})
|
||||
}
|
||||
return graph
|
||||
}
|
||||
|
||||
for (const example of AUTHORING_EXAMPLES) {
|
||||
test(`shared authoring example: ${example.title}`, async () => {
|
||||
const graph = makeSceneGraph()
|
||||
graph.addCollection({
|
||||
id: 'tokens',
|
||||
name: 'Tokens',
|
||||
modes: [{ modeId: 'default', name: 'Default' }],
|
||||
defaultModeId: 'default',
|
||||
variableIds: []
|
||||
})
|
||||
for (const [name, value] of [
|
||||
['Space/small', 8],
|
||||
['Space/medium', 16],
|
||||
['Type/body', 12]
|
||||
] as const) {
|
||||
graph.addVariable({
|
||||
id: name,
|
||||
name,
|
||||
type: 'FLOAT',
|
||||
collectionId: 'tokens',
|
||||
valuesByMode: { default: value },
|
||||
description: '',
|
||||
hiddenFromPublishing: false
|
||||
})
|
||||
}
|
||||
const graph = exampleGraph()
|
||||
const [result] = await renderJSX(graph, example.jsx)
|
||||
const frame = getNodeOrThrow(graph, result.id)
|
||||
expect(frame.width).toBe(280)
|
||||
|
|
@ -47,6 +57,36 @@ for (const example of AUTHORING_EXAMPLES) {
|
|||
})
|
||||
}
|
||||
|
||||
test('shared bound example retains bindings and resolves the initial inherited mode', async () => {
|
||||
const graph = exampleGraph()
|
||||
const parent = graph.createNode('FRAME', graph.getPages()[0].id, {
|
||||
variableModes: { tokens: 'compact' }
|
||||
})
|
||||
for (const example of AUTHORING_EXAMPLES) {
|
||||
await renderJSX(graph, example.jsx, { parentId: parent.id })
|
||||
}
|
||||
const bound = graph.getChildren(parent.id).find((node) => node.boundVariables.itemSpacing)
|
||||
if (!bound) throw new Error('Expected a variable-bound authoring example')
|
||||
expect(bound.itemSpacing).toBe(4)
|
||||
expect(bound.paddingTop).toBe(12)
|
||||
expect(bound.boundVariables).toMatchObject({
|
||||
itemSpacing: 'Space/small',
|
||||
paddingTop: 'Space/medium',
|
||||
paddingRight: 'Space/medium',
|
||||
paddingBottom: 'Space/medium',
|
||||
paddingLeft: 'Space/medium'
|
||||
})
|
||||
const [text] = graph.getChildren(bound.id)
|
||||
expect(text.fontSize).toBe(11)
|
||||
expect(text.lineHeight).toBe(16)
|
||||
expect(text.letterSpacing).toBe(0)
|
||||
expect(text.boundVariables).toMatchObject({
|
||||
fontSize: 'Type/body',
|
||||
lineHeight: 'Type/body-leading',
|
||||
letterSpacing: 'Type/body-tracking'
|
||||
})
|
||||
})
|
||||
|
||||
test('public exports and runtime prompts use the same authoring reference', () => {
|
||||
expect(BARREL_REFERENCE).toBe(JSX_REFERENCE)
|
||||
expect(BARREL_CODEGEN).toBe(CODEGEN_PROMPT)
|
||||
|
|
|
|||
Loading…
Reference in a new issue