fix(tools): set_font_range now produces valid style runs
- Use applyStyleToRange from text/style-runs.ts instead of naively appending runs (which created overlaps and invalid state) - Apply fontWeight from style name (e.g. 'Bold' → 700) - Apply color as fills on the style override - Previously the color param was accepted but silently ignored Fixes #214
This commit is contained in:
parent
5c0262fa80
commit
d0b5c51c5a
|
|
@ -1,9 +1,11 @@
|
|||
/* eslint-disable max-lines -- property setters share common patterns, splitting would scatter related tools */
|
||||
import { parseColor } from '../color'
|
||||
import { DEFAULT_SHADOW_COLOR } from '../constants'
|
||||
import { styleToWeight } from '../text/fonts'
|
||||
import { applyStyleToRange } from '../text/style-runs'
|
||||
import { defineTool } from './schema'
|
||||
|
||||
import type { CharacterStyleOverride, Effect, SceneNode, StyleRun } from '../scene-graph'
|
||||
import type { CharacterStyleOverride, Effect, SceneNode } from '../scene-graph'
|
||||
import type { Matrix } from '../types'
|
||||
|
||||
export const setFill = defineTool({
|
||||
|
|
@ -487,18 +489,21 @@ export const setFontRange = defineTool({
|
|||
execute: (figma, args) => {
|
||||
const node = figma.getNodeById(args.id)
|
||||
if (!node) return { error: `Node "${args.id}" not found` }
|
||||
const style: CharacterStyleOverride = {}
|
||||
if (args.family) style.fontFamily = args.family
|
||||
if (args.size) style.fontSize = args.size
|
||||
if (args.style === 'italic' || args.style === 'Italic') style.italic = true
|
||||
const run: StyleRun = {
|
||||
start: args.start,
|
||||
length: args.end - args.start,
|
||||
style
|
||||
const override: CharacterStyleOverride = {}
|
||||
if (args.family) override.fontFamily = args.family
|
||||
if (args.size) override.fontSize = args.size
|
||||
if (args.style) {
|
||||
const s = args.style.toLowerCase()
|
||||
if (s.includes('italic')) override.italic = true
|
||||
override.fontWeight = styleToWeight(args.style)
|
||||
}
|
||||
figma.graph.updateNode(node.id, {
|
||||
styleRuns: [...(figma.graph.getNode(node.id)?.styleRuns ?? []), run]
|
||||
})
|
||||
if (args.color) {
|
||||
override.fills = [{ type: 'SOLID', color: parseColor(args.color), opacity: 1, visible: true }]
|
||||
}
|
||||
const raw = figma.graph.getNode(node.id)
|
||||
if (!raw) return { error: `Node "${args.id}" not found` }
|
||||
const runs = applyStyleToRange(raw.styleRuns, args.start, args.end, override, raw.text.length)
|
||||
figma.graph.updateNode(node.id, { styleRuns: runs })
|
||||
return { id: args.id, range: { start: args.start, end: args.end } }
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -542,6 +542,47 @@ describe('set_constraints', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('set_font_range', () => {
|
||||
test('applies font style to text range and survives serialization', () => {
|
||||
const { figma, graph } = setup()
|
||||
const createText = ALL_TOOLS.find((t) => t.name === 'create_shape')!
|
||||
const setText = ALL_TOOLS.find((t) => t.name === 'set_text')!
|
||||
const setFontRange = ALL_TOOLS.find((t) => t.name === 'set_font_range')!
|
||||
|
||||
const created = createText.execute(figma, { type: 'TEXT', x: 0, y: 0, width: 200, height: 20 }) as any
|
||||
setText.execute(figma, { id: created.id, text: 'Hello World' })
|
||||
setFontRange.execute(figma, { id: created.id, start: 0, end: 5, family: 'Inter', size: 18, style: 'Bold' })
|
||||
|
||||
const node = graph.getNode(created.id)!
|
||||
expect(node.styleRuns.length).toBeGreaterThan(0)
|
||||
for (const run of node.styleRuns) {
|
||||
expect(run.style).toBeDefined()
|
||||
expect(typeof run.start).toBe('number')
|
||||
expect(typeof run.length).toBe('number')
|
||||
}
|
||||
const boldRun = node.styleRuns.find((r) => r.style.fontWeight === 700)
|
||||
expect(boldRun).toBeDefined()
|
||||
expect(boldRun!.start).toBe(0)
|
||||
expect(boldRun!.length).toBe(5)
|
||||
})
|
||||
|
||||
test('applies color to text range', () => {
|
||||
const { figma, graph } = setup()
|
||||
const createText = ALL_TOOLS.find((t) => t.name === 'create_shape')!
|
||||
const setText = ALL_TOOLS.find((t) => t.name === 'set_text')!
|
||||
const setFontRange = ALL_TOOLS.find((t) => t.name === 'set_font_range')!
|
||||
|
||||
const created = createText.execute(figma, { type: 'TEXT', x: 0, y: 0, width: 200, height: 20 }) as any
|
||||
setText.execute(figma, { id: created.id, text: 'Red text' })
|
||||
setFontRange.execute(figma, { id: created.id, start: 0, end: 3, color: '#ff0000' })
|
||||
|
||||
const node = graph.getNode(created.id)!
|
||||
const colorRun = node.styleRuns.find((r) => r.style.fills?.length)
|
||||
expect(colorRun).toBeDefined()
|
||||
expect(colorRun!.style.fills![0].color.r).toBeCloseTo(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
test('renders JSX string', async () => {
|
||||
const { figma } = setup()
|
||||
|
|
|
|||
Loading…
Reference in a new issue