### Font Metrics Database Structure with Capsize Integration Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Creates and utilizes a font metrics database combining Google Fonts and system fonts for Capsize calculations. Shows database structure, example metrics, and integration with Figma text nodes to compute precise pixel margins for leading trim. ```typescript // File: src/metrics/fontMetricsByFamilyName.ts import { FontMetrics } from "@capsizecss/core"; import googleFonts from "./googleFonts.json"; import systemFonts from "./systemFonts.json"; export const fontMetricsByFamilyName: Record = { ...googleFonts, ...systemFonts, }; // Example metrics structure const exampleMetrics = { "Inter": { capHeight: 2048, // Height of capital letters in font units ascent: 2728, // Highest point above baseline in font units descent: -680, // Lowest point below baseline (negative value) lineGap: 0, // Additional spacing between lines unitsPerEm: 2816 // Number of font units per em square }, "Roboto": { capHeight: 1456, ascent: 1900, descent: -500, lineGap: 0, unitsPerEm: 2048 } }; // Using metrics with Capsize import { precomputeValues } from "@capsizecss/core"; const textNode = figma.createText(); await figma.loadFontAsync({ family: "Inter", style: "Regular" }); textNode.fontName = { family: "Inter", style: "Regular" }; textNode.fontSize = 16; const fontMetrics = fontMetricsByFamilyName["Inter"]; const capsizeValues = precomputeValues({ fontSize: textNode.fontSize, leading: 24, // Line height in pixels fontMetrics: fontMetrics }); // Calculate pixel margins from Capsize values const marginTop = Math.round( parseFloat(capsizeValues.capHeightTrim) * textNode.fontSize ); const marginBottom = Math.round( parseFloat(capsizeValues.baselineTrim) * textNode.fontSize ); console.log(`Top margin: ${marginTop}px, Bottom margin: ${marginBottom}px`); // Output: Top margin: 3px, Bottom margin: 2px ``` -------------------------------- ### Open Interactive UI Panel for Text Trimming (TypeScript) Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt This command opens a UI panel allowing users to interactively adjust font size and line height for selected text. It listens for UI events to update text properties and monitors selection changes to reflect current text data in the UI. Dependencies include @create-figma-plugin/utilities. ```typescript // File: src/commands/showPanel/showPanel-main.ts import { on, emit, showUI } from "@create-figma-plugin/utilities"; import { trimSelectedNodes } from "../../core"; export default function () { // Handle trim button click from UI on("TRIM_SELECTION", () => trimSelectedNodes()); // Handle font size update from UI on("UPDATE_FONT_SIZE_FOR_SELECTION", (fontSize) => { trimSelectedNodes({ fontSize }); }); // Handle line height update from UI on("UPDATE_LINE_HEIGHT_FOR_SELECTION", (lineHeight) => { trimSelectedNodes({ lineHeight }); }); // Monitor selection changes and update UI figma.on("selectionchange", () => { const selectionData = getSelectionData(); emit("SELECTION_CHANGED", selectionData); }); // Show UI with initial selection data showUI( { width: 240, height: 108, title: "Leading Trim" }, getSelectionData() ); } // Selection data structure returned to UI function getSelectionData() { return { fontSize: 16, // Current font size (undefined if mixed) lineHeight: { unit: "PIXELS", value: 24 }, autoLineHeight: 22, // Calculated auto line height hasTextSelected: true, // Whether any text is selected hasSupportedFontSelected: true // Whether selected font is supported }; } ``` -------------------------------- ### Preact UI with Event Handlers in TypeScript Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Implements an interactive Preact UI for a Figma plugin. It syncs with Figma selection changes, handles user input for font size, and emits events for actions like trimming selection or updating font size. It uses Preact hooks and @create-figma-plugin utilities for state management and event emission/listening. ```typescript // File: src/commands/showPanel/showPanel-ui.tsx import { Button, Textbox, Container } from "@create-figma-plugin/ui"; import { emit, on } from "@create-figma-plugin/utilities"; import { h } from "preact"; import { useState, useEffect } from "preact/hooks"; export default function Plugin({ fontSize: initialFontSize, lineHeight: initialLineHeight, hasTextSelected: initialHasTextSelected, hasSupportedFontSelected: initialHasSupportedFontSelected, }) { const [fontSizeFieldValue, setFontSizeFieldValue] = useState( initialFontSize ? String(initialFontSize) : "" ); const [hasTextSelected, setHasTextSelected] = useState(initialHasTextSelected); const [hasSupportedFontSelected, setHasSupportedFontSelected] = useState( initialHasSupportedFontSelected ); // Listen for selection changes from plugin main thread useEffect(() => { on("SELECTION_CHANGED", ({ fontSize, hasTextSelected, hasSupportedFontSelected }) => { setFontSizeFieldValue(fontSize ? String(fontSize) : ""); setHasTextSelected(hasTextSelected); setHasSupportedFontSelected(hasSupportedFontSelected); }); }, []); // Handle trim button click const handleTrimButtonClick = () => { emit("TRIM_SELECTION"); }; // Handle font size change with validation const handleFontSizeChange = (value) => { if (/^[1-9][0-9]*$/.test(value)) { setFontSizeFieldValue(value); emit("UPDATE_FONT_SIZE_FOR_SELECTION", parseInt(value)); return true; } return false; // Invalid input }; // Keyboard shortcuts: Arrow up/down to increment/decrement // Shift + Arrow for +/-10 const handleKeyDown = (event) => { if (event.key === "ArrowUp" || event.key === "ArrowDown") { event.preventDefault(); const currentValue = parseInt(fontSizeFieldValue); const increment = event.shiftKey ? 10 : 1; const newValue = Math.max( 1, currentValue + (event.key === "ArrowDown" ? -increment : increment) ); setFontSizeFieldValue(String(newValue)); emit("UPDATE_FONT_SIZE_FOR_SELECTION", newValue); } }; return (
setFontSizeFieldValue(e.currentTarget.value)} validateOnBlur={handleFontSizeChange} value={fontSizeFieldValue} />
); } ``` -------------------------------- ### Retrieve font metrics for text node Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Retrieves font metrics required for Capsize calculations from the internal font metrics database. Returns null for fonts not in the database. ```typescript import { getFontMetricsForTextNode } from "./core"; // Get metrics for a text node const textNode = figma.currentPage.selection[0] as TextNode; const fontMetrics = getFontMetricsForTextNode(textNode); if (fontMetrics) { console.log(fontMetrics); // Output: // { // capHeight: 1456, // ascent: 1900, // descent: -500, // lineGap: 0, // unitsPerEm: 2048 // } // Calculate auto line height manually const { descent, ascent, lineGap, unitsPerEm } = fontMetrics; const absoluteDescent = Math.abs(descent); const contentArea = ascent + lineGap + absoluteDescent; const lineHeightScale = contentArea / unitsPerEm; const autoLineHeight = Math.round(lineHeightScale * textNode.fontSize); console.log(`Auto line height: ${autoLineHeight}px`); } else { console.log("Font not supported"); // Returns null for fonts not in the metrics database } ``` -------------------------------- ### Text Node Validation and Error Handling Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Comprehensive input validation patterns for Figma text nodes including mixed font detection, unsupported font validation, font size validation, line height parsing, and auto-resize handling. Provides user-friendly error messages and graceful fallbacks for invalid inputs. ```typescript // Mixed font validation const textNode = figma.currentPage.selection[0] as TextNode; if (textNode.fontName === figma.mixed) { const fontFamilies = new Set( textNode .getRangeAllFontNames(0, textNode.characters.length) .map((fontName) => fontName.family) ); if (fontFamilies.size > 1) { figma.notify("Leading cannot be trimmed from text with mixed fonts.", { timeout: 5000 }); // Abort operation } } // Unsupported font validation import { fontMetricsByFamilyName } from "./metrics/fontMetricsByFamilyName"; const fontFamily = textNode.fontName.family; if (!(fontFamily in fontMetricsByFamilyName)) { figma.notify( `Leading cannot be trimmed from font "${fontFamily}" as it is not currently supported.`, { timeout: 5000 } ); // Abort operation } // Mixed font size validation if (textNode.fontSize === figma.mixed) { figma.notify("Leading cannot be trimmed from text with mixed sizes.", { timeout: 5000 }); // Abort operation } // Mixed line height validation if (textNode.lineHeight === figma.mixed) { figma.notify("Leading cannot be trimmed from text with mixed line heights.", { timeout: 5000 }); // Abort operation } // Auto-resize handling if (textNode.textAutoResize === "NONE") { textNode.textAutoResize = "HEIGHT"; // Ensures text can expand vertically as needed } // Line height parsing with error handling function parseLineHeight(lineHeightString: string): LineHeight | null { if (lineHeightString === "Auto") { return { unit: "AUTO" }; } // Validate pixel format: "24", "32", etc. if (/^[1-9][0-9]*$/.test(lineHeightString)) { return { unit: "PIXELS", value: parseInt(lineHeightString) }; } // Validate percentage format: "120%", "150%", etc. if (/^[1-9][0-9]*%$/.test(lineHeightString)) { return { unit: "PERCENT", value: parseInt(lineHeightString) }; } return null; // Invalid format } // Example usage const result1 = parseLineHeight("24"); // { unit: "PIXELS", value: 24 } const result2 = parseLineHeight("120%"); // { unit: "PERCENT", value: 120 } const result3 = parseLineHeight("Auto"); // { unit: "AUTO" } const result4 = parseLineHeight("abc"); // null (invalid) ``` -------------------------------- ### TypeScript Interfaces for Figma Plugin Event System Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Defines TypeScript interfaces for type-safe communication between the UI and the main thread of a Figma plugin. It includes types for selection data and various event handlers, ensuring robust event handling and data consistency. ```typescript // File: src/types.ts import { EventHandler } from "@create-figma-plugin/utilities"; // Selection state passed from main thread to UI export type SelectionData = { fontSize?: number; // Current font size (undefined if mixed) lineHeight?: LineHeight; // Current line height (undefined if mixed) autoLineHeight?: number; // Calculated auto line height in pixels hasTextSelected: boolean; // Whether any text is selected hasSupportedFontSelected: boolean; // Whether font is in metrics database }; // Event: Trim button clicked in UI export interface TrimSelectionHandler extends EventHandler { name: "TRIM_SELECTION"; handler: () => void; } // Event: Font size changed in UI export interface UpdateFontSizeHandler extends EventHandler { name: "UPDATE_FONT_SIZE_FOR_SELECTION"; handler: (fontSize: number) => void; } // Event: Line height changed in UI export interface UpdateLineHeightHandler extends EventHandler { name: "UPDATE_LINE_HEIGHT_FOR_SELECTION"; handler: (lineHeight: LineHeight) => void; } // Event: Selection changed in Figma canvas export interface SelectionChangedHandler extends EventHandler { name: "SELECTION_CHANGED"; handler: (selectionData: SelectionData) => void; } // Usage example: Type-safe event emission import { emit } from "@create-figma-plugin/utilities"; // From UI to main threademit("TRIM_SELECTION"); emit("UPDATE_FONT_SIZE_FOR_SELECTION", 24); emit("UPDATE_LINE_HEIGHT_FOR_SELECTION", { unit: "PIXELS", value: 32 }); // From main thread to UI emit("SELECTION_CHANGED", { fontSize: 16, lineHeight: { unit: "AUTO" }, autoLineHeight: 22, hasTextSelected: true, hasSupportedFontSelected: true }); ``` -------------------------------- ### Trim Selected Text Directly and Close Plugin (TypeScript) Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt This command trims the currently selected text nodes immediately without opening a UI panel and then closes the plugin. It's designed for quick application of trimming with default settings. This function relies on the `trimSelectedNodes` utility from the core module. ```typescript // File: src/commands/trimSelection.ts import { trimSelectedNodes } from "../core"; export default async function () { await trimSelectedNodes(); figma.closePlugin(); } // Usage in Figma: // 1. Select text node with "Inter" font at 16px // 2. Run "Trim selection" command // 3. Text is wrapped in frame with calculated margins // 4. Plugin closes automatically // Example result: // Before: TextNode { y: 100, height: 22 } // After: FrameNode { // y: 97, // Adjusted for top margin // height: 25, // text height + margins // children: [ // TextNode { // y: 3, // Top margin (marginTop) // height: 22 // Original text height // } // ] // } ``` -------------------------------- ### Trim selected text nodes with custom options Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Performs the leading trim operation on selected text nodes by wrapping them in frames with calculated offsets. Supports custom font size and line height settings. ```typescript import { trimSelectedNodes } from "./core"; // Basic usage - trim with current font size and line height await trimSelectedNodes(); // Trim with custom font size await trimSelectedNodes({ fontSize: 24 }); // Trim with custom line height (pixels) await trimSelectedNodes({ lineHeight: { unit: "PIXELS", value: 32 } }); // Trim with custom line height (percentage) await trimSelectedNodes({ lineHeight: { unit: "PERCENT", value: 120 } }); // Trim with auto line height await trimSelectedNodes({ lineHeight: { unit: "AUTO" } }); // Trim with both custom font size and line height await trimSelectedNodes({ fontSize: 18, lineHeight: { unit: "PIXELS", value: 24 } }); // Example: Update already-trimmed text // When text is already trimmed, calling trimSelectedNodes again // updates the frame and margins without creating a new wrapper figma.currentPage.selection = [existingTrimmedFrame]; await trimSelectedNodes({ fontSize: 32 }); // Result: Frame resized, text repositioned with new margins ``` -------------------------------- ### Resolve text node from selected node Source: https://context7.com/markdalgleish/figma-leading-trim/llms.txt Extracts the text node from either a direct text selection or a previously-trimmed frame containing text. Returns undefined for non-text selections or frames not owned by the plugin. ```typescript import { resolveTextNodeFromSelectedNode } from "./core"; // Example 1: Direct text selection const textNode = figma.createText(); textNode.characters = "Hello World"; await figma.loadFontAsync({ family: "Inter", style: "Regular" }); textNode.fontName = { family: "Inter", style: "Regular" }; const resolved = resolveTextNodeFromSelectedNode(textNode); console.log(resolved === textNode); // true // Example 2: Previously-trimmed frame selection // After trimming, the text is wrapped in a frame marked as "owned" figma.currentPage.selection = [textNode]; await trimSelectedNodes(); // Creates frame wrapper const trimmedFrame = figma.currentPage.selection[0]; const resolvedFromFrame = resolveTextNodeFromSelectedNode(trimmedFrame); console.log(resolvedFromFrame.type); // "TEXT" console.log(resolvedFromFrame.parent === trimmedFrame); // true // Example 3: Non-text selection returns undefined const rectangle = figma.createRectangle(); const result = resolveTextNodeFromSelectedNode(rectangle); console.log(result); // undefined // Example 4: Frame not owned by plugin returns undefined const customFrame = figma.createFrame(); customFrame.appendChild(textNode); const notOwned = resolveTextNodeFromSelectedNode(customFrame); console.log(notOwned); // undefined ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.