feat: emit textAutoResize and text truncation in design tree#217
feat: emit textAutoResize and text truncation in design tree#217
Conversation
Add text sizing/wrapping properties to design-tree output: - textAutoResize → text-resize: auto | fixed-height | truncate - textTruncation ENDING → text-overflow: ellipsis - maxLines → max-lines: N - paragraphSpacing → paragraph-spacing: Npx Also adds textTruncation and maxLines to AnalysisNode schema. https://claude.ai/code/session_01PqgYRCyuLA48EzyFWnHM82
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe PR adds optional text fields ( Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/core/contracts/figma-node.ts`:
- Around line 158-159: The transformer in
src/core/contracts/figma-transformer.ts needs to populate the new text fields:
when handling TEXT nodes (the block that currently extracts characters and
style), also read node.textTruncation and node.maxLines from the Figma API
response and set them on the transformed object (e.g., add textTruncation and
maxLines properties alongside characters and style). Ensure textTruncation maps
to the schema enum values ("DISABLED" | "ENDING") and maxLines is set as an
optional number only when present; leave them undefined otherwise so existing
null-checks (node.maxLines != null) continue to work.
In `@src/core/design-tree/design-tree.test.ts`:
- Around line 1093-1200: Add three edge-case tests to the existing "text
auto-resize and truncation" suite using makeFile/makeNode and
generateDesignTree: (1) a TRUNCATE text node without maxLines that asserts
output contains "text-resize: truncate" and does NOT contain "max-lines:"; (2) a
text node with textTruncation: "DISABLED" (and textAutoResize e.g. "HEIGHT")
that asserts output does NOT contain "text-overflow:"; (3) a text node with
style.paragraphSpacing: 0 that asserts output does NOT contain
"paragraph-spacing:". Use unique test names like "emits text-resize: truncate
without max-lines", "does not emit text-overflow for DISABLED", and "does not
emit paragraph-spacing for 0" to match the existing pattern.
In `@src/core/design-tree/design-tree.ts`:
- Around line 493-519: The TRUNCATE branch currently sets "text-resize:
truncate" and emits `max-lines` but omits the ellipsis; update the logic in the
block that checks textAutoResize === "TRUNCATE" (where textAutoResize and
node.maxLines are used) to also push "text-overflow: ellipsis" so TRUNCATE mode
emits both `text-resize: truncate` and `text-overflow: ellipsis` (keep the
existing guarded emission of `max-lines` based on node.maxLines); ensure you do
not duplicate ellipsis when the later conditional for textAutoResize !==
"TRUNCATE" runs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 74f126a6-4843-448c-bfaa-d3751599d0a0
📒 Files selected for processing (3)
src/core/contracts/figma-node.tssrc/core/design-tree/design-tree.test.tssrc/core/design-tree/design-tree.ts
| describe("text auto-resize and truncation", () => { | ||
| it("emits text-resize: auto for WIDTH_AND_HEIGHT", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Title", | ||
| type: "TEXT", | ||
| characters: "Hello", | ||
| style: { fontFamily: "Inter", textAutoResize: "WIDTH_AND_HEIGHT" }, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 100, height: 20 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).toContain("text-resize: auto"); | ||
| }); | ||
|
|
||
| it("emits text-resize: fixed-height for HEIGHT", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Body", | ||
| type: "TEXT", | ||
| characters: "Wrapped text", | ||
| style: { fontFamily: "Inter", textAutoResize: "HEIGHT" }, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 300, height: 48 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).toContain("text-resize: fixed-height"); | ||
| }); | ||
|
|
||
| it("emits text-resize: truncate with max-lines for TRUNCATE", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Truncated", | ||
| type: "TEXT", | ||
| characters: "Long text that gets cut off", | ||
| style: { fontFamily: "Inter", textAutoResize: "TRUNCATE" }, | ||
| maxLines: 2, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 200, height: 40 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).toContain("text-resize: truncate"); | ||
| expect(output).toContain("max-lines: 2"); | ||
| }); | ||
|
|
||
| it("emits text-overflow: ellipsis when textTruncation is ENDING", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Ellipsis", | ||
| type: "TEXT", | ||
| characters: "Truncated text", | ||
| style: { fontFamily: "Inter", textAutoResize: "HEIGHT" }, | ||
| textTruncation: "ENDING", | ||
| maxLines: 3, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 200, height: 60 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).toContain("text-overflow: ellipsis"); | ||
| expect(output).toContain("max-lines: 3"); | ||
| }); | ||
|
|
||
| it("emits paragraph-spacing when set", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Paragraphs", | ||
| type: "TEXT", | ||
| characters: "First paragraph\n\nSecond paragraph", | ||
| style: { fontFamily: "Inter", paragraphSpacing: 16 }, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 300, height: 100 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).toContain("paragraph-spacing: 16px"); | ||
| }); | ||
|
|
||
| it("does not emit text-resize for NONE or missing textAutoResize", () => { | ||
| const file = makeFile( | ||
| makeNode({ | ||
| id: "1:1", | ||
| name: "Plain", | ||
| type: "TEXT", | ||
| characters: "No resize", | ||
| style: { fontFamily: "Inter" }, | ||
| absoluteBoundingBox: { x: 0, y: 0, width: 100, height: 20 }, | ||
| }) | ||
| ); | ||
|
|
||
| const output = generateDesignTree(file); | ||
|
|
||
| expect(output).not.toContain("text-resize:"); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Good test coverage with a few edge cases to consider.
The test suite covers the main scenarios well. Consider adding these edge cases for completeness:
TRUNCATEmode withoutmaxLines- verifies onlytext-resize: truncateis emittedtextTruncation: "DISABLED"- verifies notext-overflow: ellipsisis emittedparagraphSpacing: 0- verifies noparagraph-spacingis emitted (falsy value)
💡 Optional: Additional edge case tests
it("emits text-resize: truncate without max-lines when maxLines is not set", () => {
const file = makeFile(
makeNode({
id: "1:1",
name: "TruncateNoMax",
type: "TEXT",
characters: "Truncated",
style: { fontFamily: "Inter", textAutoResize: "TRUNCATE" },
absoluteBoundingBox: { x: 0, y: 0, width: 200, height: 40 },
})
);
const output = generateDesignTree(file);
expect(output).toContain("text-resize: truncate");
expect(output).not.toContain("max-lines:");
});
it("does not emit text-overflow for textTruncation: DISABLED", () => {
const file = makeFile(
makeNode({
id: "1:1",
name: "NoTruncation",
type: "TEXT",
characters: "Normal text",
style: { fontFamily: "Inter", textAutoResize: "HEIGHT" },
textTruncation: "DISABLED",
absoluteBoundingBox: { x: 0, y: 0, width: 200, height: 40 },
})
);
const output = generateDesignTree(file);
expect(output).not.toContain("text-overflow:");
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/core/design-tree/design-tree.test.ts` around lines 1093 - 1200, Add three
edge-case tests to the existing "text auto-resize and truncation" suite using
makeFile/makeNode and generateDesignTree: (1) a TRUNCATE text node without
maxLines that asserts output contains "text-resize: truncate" and does NOT
contain "max-lines:"; (2) a text node with textTruncation: "DISABLED" (and
textAutoResize e.g. "HEIGHT") that asserts output does NOT contain
"text-overflow:"; (3) a text node with style.paragraphSpacing: 0 that asserts
output does NOT contain "paragraph-spacing:". Use unique test names like "emits
text-resize: truncate without max-lines", "does not emit text-overflow for
DISABLED", and "does not emit paragraph-spacing for 0" to match the existing
pattern.
- Populate textTruncation and maxLines in figma-transformer.ts - Emit text-overflow: ellipsis for TRUNCATE mode - Add 3 edge case tests (TRUNCATE without maxLines, DISABLED, paragraphSpacing: 0) https://claude.ai/code/session_01PqgYRCyuLA48EzyFWnHM82
Summary
textAutoResizeastext-resize: auto | fixed-height | truncatein design-tree outputtextTruncation: ENDINGastext-overflow: ellipsismaxLinesasmax-lines: N(for truncated/clamped text)paragraphSpacingasparagraph-spacing: NpxtextTruncationandmaxLinestoAnalysisNodeschema (Figma API fields)Context
The design tree omitted text sizing/wrapping properties critical for accurate implementation. AI couldn't distinguish auto-width vs fixed-width text, or know when text should show ellipsis. Identified in #200 Score Adjustment Interview.
Design tree output examples
Fixture data validation
Across all 24 fixtures (1420 TEXT nodes):
textAutoResize: 1044 WIDTH_AND_HEIGHT, 374 HEIGHT, 2 undefinedtextTruncation,maxLines,paragraphSpacing: 0 occurrences in current fixtures (but supported by Figma API)Test plan
pnpm test:run— 687 tests passedpnpm lint— cleanCloses #216
Summary by CodeRabbit
New Features
Tests