fix(set_layout): default to HUG sizing when enabling auto-layout (#125)
* fix(set_layout): default to HUG sizing when enabling auto-layout
When `set_layout` transitions a frame from `layoutMode: 'NONE'` to
auto-layout, it now sets `primaryAxisSizingMode` and
`counterAxisSizingMode` to `'AUTO'` (which maps to HUG internally).
Previously these remained at `'FIXED'`, causing the frame to keep its
original dimensions instead of shrinking/growing to fit children. This
made `h="hug"`, `justify="end"`, and `grow={1}` appear broken when
containers were created via the `set_layout` MCP tool (as opposed to
the JSX `render` path, which already defaulted to HUG).
The fix only applies when `direction` is provided and the frame was
previously in `layoutMode: 'NONE'` — updating spacing or alignment on
an existing auto-layout frame does not reset sizing modes.
* docs: add changelog entry for set_layout HUG sizing fix
This commit is contained in:
parent
63e204fc71
commit
a8fc8f1ea8
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix `set_layout` tool not defaulting to HUG sizing when enabling auto-layout — frames now shrink/grow to fit children instead of keeping fixed dimensions
|
||||
|
||||
## 0.10.0 — 2026-03-15
|
||||
|
||||
### Performance
|
||||
|
|
|
|||
|
|
@ -229,7 +229,12 @@ export const setLayout = defineTool({
|
|||
return { error: 'Frame has no auto-layout. Pass direction ("HORIZONTAL" or "VERTICAL") to enable it.' }
|
||||
}
|
||||
|
||||
const wasNone = raw?.layoutMode === 'NONE'
|
||||
if (args.direction) node.layoutMode = args.direction as 'HORIZONTAL' | 'VERTICAL'
|
||||
if (args.direction && wasNone) {
|
||||
node.primaryAxisSizingMode = 'AUTO'
|
||||
node.counterAxisSizingMode = 'AUTO'
|
||||
}
|
||||
if (args.spacing !== undefined) node.itemSpacing = args.spacing
|
||||
if (args.align !== undefined) node.primaryAxisAlignItems = args.align
|
||||
if (args.counter_align !== undefined) node.counterAxisAlignItems = args.counter_align
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import { ALL_TOOLS, FigmaAPI, SceneGraph } from '@open-pencil/core'
|
||||
import { ALL_TOOLS, FigmaAPI, SceneGraph, computeAllLayouts } from '@open-pencil/core'
|
||||
|
||||
function setup() {
|
||||
const graph = new SceneGraph()
|
||||
|
|
@ -216,6 +216,67 @@ describe('set_layout', () => {
|
|||
expect(node.paddingLeft).toBe(20)
|
||||
expect(node.paddingTop).toBe(20)
|
||||
})
|
||||
|
||||
test('defaults to HUG sizing when enabling auto-layout', () => {
|
||||
const { graph, figma } = setup()
|
||||
const frame = figma.createFrame()
|
||||
frame.resize(300, 200)
|
||||
|
||||
const rawBefore = graph.getNode(frame.id)!
|
||||
expect(rawBefore.layoutMode).toBe('NONE')
|
||||
expect(rawBefore.primaryAxisSizing).toBe('FIXED')
|
||||
expect(rawBefore.counterAxisSizing).toBe('FIXED')
|
||||
|
||||
const tool = ALL_TOOLS.find((t) => t.name === 'set_layout')!
|
||||
tool.execute(figma, { id: frame.id, direction: 'VERTICAL' })
|
||||
|
||||
const rawAfter = graph.getNode(frame.id)!
|
||||
expect(rawAfter.layoutMode).toBe('VERTICAL')
|
||||
expect(rawAfter.primaryAxisSizing).toBe('HUG')
|
||||
expect(rawAfter.counterAxisSizing).toBe('HUG')
|
||||
})
|
||||
|
||||
test('preserves sizing modes when updating existing auto-layout', () => {
|
||||
const { graph, figma } = setup()
|
||||
const frame = figma.createFrame()
|
||||
frame.resize(300, 200)
|
||||
|
||||
const tool = ALL_TOOLS.find((t) => t.name === 'set_layout')!
|
||||
tool.execute(figma, { id: frame.id, direction: 'HORIZONTAL' })
|
||||
|
||||
// Manually set primary axis back to FIXED (user wants fixed width)
|
||||
graph.updateNode(frame.id, { primaryAxisSizing: 'FIXED' })
|
||||
expect(graph.getNode(frame.id)!.primaryAxisSizing).toBe('FIXED')
|
||||
|
||||
// Updating spacing should NOT reset sizing modes
|
||||
tool.execute(figma, { id: frame.id, spacing: 24 })
|
||||
|
||||
const raw = graph.getNode(frame.id)!
|
||||
expect(raw.primaryAxisSizing).toBe('FIXED')
|
||||
expect(raw.counterAxisSizing).toBe('HUG')
|
||||
})
|
||||
|
||||
test('HUG default enables hug-to-fit with 5 children', () => {
|
||||
const { graph, figma } = setup()
|
||||
const frame = figma.createFrame()
|
||||
frame.resize(320, 100)
|
||||
|
||||
const tool = ALL_TOOLS.find((t) => t.name === 'set_layout')!
|
||||
tool.execute(figma, { id: frame.id, direction: 'VERTICAL', spacing: 8, padding: 16 })
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const child = figma.createRectangle()
|
||||
child.resize(280, 50)
|
||||
frame.appendChild(child)
|
||||
}
|
||||
|
||||
computeAllLayouts(graph)
|
||||
|
||||
const node = graph.getNode(frame.id)!
|
||||
expect(node.primaryAxisSizing).toBe('HUG')
|
||||
// 16 (top pad) + 5*50 (children) + 4*8 (gaps) + 16 (bottom pad) = 314
|
||||
expect(node.height).toBe(314)
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete_node', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue