Skip to content

Commit 635f78f

Browse files
committed
refactor: replace applyVariableTransforms with transform function and clean up variable handling, fix backgrond-position
1 parent 326b9a1 commit 635f78f

File tree

7 files changed

+180
-187
lines changed

7 files changed

+180
-187
lines changed

packages/extension/mcp/tools/code/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { collectCandidateVariableIds, resolveTokenDefsByNames } from '../token'
2525
import { collectSceneData } from './collect'
2626
import { buildGetCodeMessage } from './messages'
2727
import { renderSemanticNode } from './render'
28-
import { applyVariableTransforms } from './style'
28+
import { transform } from './variables'
2929

3030
// Tags that should render children without extra whitespace/newlines.
3131
const COMPACT_TAGS = new Set([
@@ -127,7 +127,7 @@ export async function handleGetCode(
127127
const assetRegistry = new Map<string, AssetDescriptor>()
128128
const { nodes: nodeMap, styles, svgs } = await collectSceneData(tree.roots, config, assetRegistry)
129129

130-
const styleVarNames = await applyVariableTransforms(styles, {
130+
const styleVarNames = await transform(styles, {
131131
pluginCode,
132132
config
133133
})

packages/extension/mcp/tools/code/style.ts

Lines changed: 1 addition & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
import type { CodegenConfig } from '@/utils/codegen'
22

3-
import { runTransformVariableBatch } from '@/mcp/transform-variables/requester'
4-
import { workerUnitOptions } from '@/utils/codegen'
53
import {
6-
canonicalizeVarName,
74
expandShorthands,
8-
extractVarNames,
95
formatHexAlpha,
10-
normalizeCssVarName,
11-
preprocessCssValue,
12-
replaceVarFunctions,
13-
stripFallback,
146
normalizeStyleValues,
15-
parseBackgroundShorthand,
16-
toVarExpr
7+
parseBackgroundShorthand
178
} from '@/utils/css'
189
import { cssToClassNames } from '@/utils/tailwind'
1910

@@ -338,63 +329,6 @@ function hasAtomicPadding(s: Record<string, string>) {
338329
return !!(s['padding-top'] || s['padding-right'] || s['padding-bottom'] || s['padding-left'])
339330
}
340331

341-
type VariableReferenceInternal = {
342-
nodeId: string
343-
property: string
344-
code: string
345-
name: string
346-
value?: string
347-
}
348-
349-
type PropertyBucket = {
350-
nodeId: string
351-
property: string
352-
value: string
353-
matchIndices: number[]
354-
}
355-
356-
type ApplyVariableOptions = {
357-
pluginCode?: string
358-
config: CodegenConfig
359-
}
360-
361-
export async function applyVariableTransforms(
362-
styles: Map<string, Record<string, string>>,
363-
{ pluginCode, config }: ApplyVariableOptions
364-
): Promise<Set<string>> {
365-
const { references, buckets } = collectVariableReferences(styles)
366-
if (!references.length) return new Set()
367-
368-
const transformResults = await runTransformVariableBatch(
369-
references.map(({ code, name, value }) => ({ code, name, value })),
370-
workerUnitOptions(config),
371-
pluginCode
372-
)
373-
374-
const replacements = transformResults.map((result) => {
375-
const noFallback = stripFallback(result)
376-
const canonicalName = canonicalizeVarName(noFallback)
377-
return canonicalName ? toVarExpr(canonicalName) : noFallback
378-
})
379-
const usedNames = new Set<string>()
380-
// Keep original reference names to handle transforms that inline literal values.
381-
references.forEach(({ name }) => usedNames.add(`--${name}`))
382-
replacements.forEach((repl) => extractVarNames(repl).forEach((n) => usedNames.add(n)))
383-
384-
for (const bucket of buckets.values()) {
385-
const style = styles.get(bucket.nodeId)
386-
if (!style) continue
387-
388-
let occurrence = 0
389-
style[bucket.property] = replaceVarFunctions(bucket.value, ({ full }) => {
390-
const refIndex = bucket.matchIndices[occurrence++]
391-
return refIndex != null ? (replacements[refIndex] ?? full) : full
392-
})
393-
}
394-
395-
return usedNames
396-
}
397-
398332
export function stripInertShadows(style: Record<string, string>, node: SceneNode): void {
399333
if (!style['box-shadow']) return
400334
if (hasRenderableFill(node)) return
@@ -421,51 +355,6 @@ function isFillRenderable(fill: Paint | undefined): boolean {
421355
return true
422356
}
423357

424-
function collectVariableReferences(styles: Map<string, Record<string, string>>) {
425-
const references: VariableReferenceInternal[] = []
426-
const buckets = new Map<string, PropertyBucket>()
427-
428-
for (const [nodeId, style] of styles.entries()) {
429-
for (const [property, value] of Object.entries(style)) {
430-
let hasMatch = false
431-
const indices: number[] = []
432-
433-
const normalized = preprocessCssValue(value)
434-
if (normalized !== value) {
435-
style[property] = normalized
436-
}
437-
438-
replaceVarFunctions(normalized, ({ full, name, fallback }) => {
439-
const trimmed = name.trim()
440-
if (!trimmed.startsWith('--')) return full
441-
442-
hasMatch = true
443-
const refIndex =
444-
references.push({
445-
nodeId,
446-
property,
447-
code: full,
448-
name: normalizeCssVarName(trimmed.slice(2)),
449-
value: fallback?.trim()
450-
}) - 1
451-
indices.push(refIndex)
452-
return full
453-
})
454-
455-
if (hasMatch) {
456-
const key = `${nodeId}:${property}`
457-
const bucket = buckets.get(key)
458-
if (bucket) {
459-
bucket.matchIndices.push(...indices)
460-
} else {
461-
buckets.set(key, { nodeId, property, value: normalized, matchIndices: indices })
462-
}
463-
}
464-
}
465-
}
466-
return { references, buckets }
467-
}
468-
469358
export function styleToClassNames(
470359
style: Record<string, string>,
471360
config: CodegenConfig,

packages/extension/mcp/tools/code/text/render.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { joinClassNames } from '@/utils/tailwind'
55

66
import type { RenderContext } from '../render'
77

8-
import { applyVariableTransforms, styleToClassNames } from '../style'
8+
import { styleToClassNames } from '../style'
9+
import { transform } from '../variables'
910
import { buildTextBlocks, formatTextLiteral, getStyledSegments } from './segments'
1011
import { computeDominantStyle, omitCommon } from './style'
1112
import {
@@ -53,7 +54,7 @@ export async function renderTextSegments(
5354
}
5455
}
5556

56-
await applyVariableTransforms(runStyleMap, {
57+
await transform(runStyleMap, {
5758
pluginCode: ctx.pluginCode,
5859
config: ctx.config
5960
})
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import type { CodegenConfig } from '@/utils/codegen'
2+
3+
import { runTransformVariableBatch } from '@/mcp/transform-variables/requester'
4+
import { workerUnitOptions } from '@/utils/codegen'
5+
import {
6+
canonicalizeVarName,
7+
extractVarNames,
8+
normalizeCssVarName,
9+
preprocessCssValue,
10+
replaceVarFunctions,
11+
stripFallback,
12+
toVarExpr
13+
} from '@/utils/css'
14+
15+
type VariableReferenceInternal = {
16+
nodeId: string
17+
property: string
18+
code: string
19+
name: string
20+
value?: string
21+
}
22+
23+
type PropertyBucket = {
24+
nodeId: string
25+
property: string
26+
value: string
27+
matchIndices: number[]
28+
}
29+
30+
type TransformOptions = {
31+
pluginCode?: string
32+
config: CodegenConfig
33+
}
34+
35+
export function transform(
36+
styles: Map<string, Record<string, string>>,
37+
{ pluginCode, config }: TransformOptions
38+
): Promise<Set<string>> {
39+
const { references, buckets } = collectRefs(styles)
40+
if (!references.length) return Promise.resolve(new Set())
41+
42+
return runTransformVariableBatch(
43+
references.map(({ code, name, value }) => ({ code, name, value })),
44+
workerUnitOptions(config),
45+
pluginCode
46+
).then((transformResults) => {
47+
const replacements = transformResults.map((result) => {
48+
const noFallback = stripFallback(result)
49+
const canonicalName = canonicalizeVarName(noFallback)
50+
return canonicalName ? toVarExpr(canonicalName) : noFallback
51+
})
52+
const usedNames = new Set<string>()
53+
// Keep original reference names to handle transforms that inline literal values.
54+
references.forEach(({ name }) => usedNames.add(`--${name}`))
55+
replacements.forEach((repl) => extractVarNames(repl).forEach((n) => usedNames.add(n)))
56+
57+
for (const bucket of buckets.values()) {
58+
const style = styles.get(bucket.nodeId)
59+
if (!style) continue
60+
61+
let occurrence = 0
62+
style[bucket.property] = replaceVarFunctions(bucket.value, ({ full }) => {
63+
const refIndex = bucket.matchIndices[occurrence++]
64+
return refIndex != null ? (replacements[refIndex] ?? full) : full
65+
})
66+
}
67+
68+
return usedNames
69+
})
70+
}
71+
72+
export function collectRefs(styles: Map<string, Record<string, string>>) {
73+
const references: VariableReferenceInternal[] = []
74+
const buckets = new Map<string, PropertyBucket>()
75+
76+
for (const [nodeId, style] of styles.entries()) {
77+
for (const [property, value] of Object.entries(style)) {
78+
let hasMatch = false
79+
const indices: number[] = []
80+
81+
const normalized = preprocessCssValue(value)
82+
if (normalized !== value) {
83+
style[property] = normalized
84+
}
85+
86+
replaceVarFunctions(normalized, ({ full, name, fallback }) => {
87+
const trimmed = name.trim()
88+
if (!trimmed.startsWith('--')) return full
89+
90+
hasMatch = true
91+
const refIndex =
92+
references.push({
93+
nodeId,
94+
property,
95+
code: full,
96+
name: normalizeCssVarName(trimmed.slice(2)),
97+
value: fallback?.trim()
98+
}) - 1
99+
indices.push(refIndex)
100+
return full
101+
})
102+
103+
if (hasMatch) {
104+
const key = `${nodeId}:${property}`
105+
const bucket = buckets.get(key)
106+
if (bucket) {
107+
bucket.matchIndices.push(...indices)
108+
} else {
109+
buckets.set(key, { nodeId, property, value: normalized, matchIndices: indices })
110+
}
111+
}
112+
}
113+
}
114+
return { references, buckets }
115+
}

0 commit comments

Comments
 (0)