diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca187d2e..35edd45c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/packages/core/src/tools/modify.ts b/packages/core/src/tools/modify.ts index cdea45102..b674dfc9c 100644 --- a/packages/core/src/tools/modify.ts +++ b/packages/core/src/tools/modify.ts @@ -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 diff --git a/tests/engine/tools.test.ts b/tests/engine/tools.test.ts index 8da7c2dc1..d3a42efac 100644 --- a/tests/engine/tools.test.ts +++ b/tests/engine/tools.test.ts @@ -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', () => {