Remove autoresearch scaffolding
Keeping the actual fixes (instance overrides, constraint scaling, renderer improvements) — only removing experiment infrastructure.
This commit is contained in:
parent
152b0d1666
commit
e713eaa37a
|
|
@ -1,125 +0,0 @@
|
|||
/**
|
||||
* Compare .fig import against Figma ground truth.
|
||||
*
|
||||
* Node IDs differ between .fig import and Figma Plugin API, so we match
|
||||
* nodes by tree path: sequence of (name, sibling_index) from root to leaf.
|
||||
*
|
||||
* Truth: tests/fixtures/gold-preview-truth.json (extracted from live Figma).
|
||||
*/
|
||||
import { readFigFile } from './packages/core/src/kiwi/fig-file'
|
||||
import { FigmaAPI } from './packages/core/src/figma-api'
|
||||
|
||||
const fixturePath = process.argv[2] || 'tests/fixtures/gold-preview.fig'
|
||||
const truthPath = 'tests/fixtures/gold-preview-truth.json'
|
||||
|
||||
interface TruthNode {
|
||||
path: string
|
||||
name: string
|
||||
type: string
|
||||
visible: boolean
|
||||
width: number
|
||||
height: number
|
||||
text?: string
|
||||
fill?: string
|
||||
cr?: number
|
||||
clip?: boolean
|
||||
}
|
||||
|
||||
function colorHex(c: { r: number; g: number; b: number; a: number }): string {
|
||||
const r = Math.round(c.r * 255)
|
||||
const g = Math.round(c.g * 255)
|
||||
const b = Math.round(c.b * 255)
|
||||
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const truth: TruthNode[] = JSON.parse(await Bun.file(truthPath).text())
|
||||
const truthMap = new Map(truth.map(n => [n.path, n]))
|
||||
|
||||
const file = Bun.file(fixturePath)
|
||||
const graph = await readFigFile(new File([await file.arrayBuffer()], 'gold-preview.fig'))
|
||||
|
||||
const api = new FigmaAPI(graph)
|
||||
const page = api.root.children[0]
|
||||
if (!page) { console.error('No page found'); process.exit(1) }
|
||||
|
||||
const ourNodes = new Map<string, TruthNode>()
|
||||
|
||||
function collect(proxy: ReturnType<FigmaAPI['wrapNode']>, parentPath: string, sibIdx: number, sibNames: Map<string, number>) {
|
||||
// Use name-based index when all sibling names are unique, numeric index otherwise
|
||||
const nameCount = sibNames.get(proxy.name) ?? 0
|
||||
sibNames.set(proxy.name, nameCount + 1)
|
||||
const path = `${parentPath}/${proxy.name}[${nameCount}]`
|
||||
const raw = (proxy as unknown as { _raw(): Record<string, unknown> })._raw()
|
||||
const fills = raw.fills as Array<{ type: string; color: { r: number; g: number; b: number; a: number }; visible: boolean }> | undefined
|
||||
const visibleFill = fills?.find(f => f.type === 'SOLID' && f.visible !== false)
|
||||
|
||||
const entry: TruthNode = {
|
||||
path,
|
||||
name: proxy.name,
|
||||
type: proxy.type,
|
||||
visible: (proxy as unknown as { visible: boolean }).visible,
|
||||
width: Math.round((raw.width as number) * 100) / 100,
|
||||
height: Math.round((raw.height as number) * 100) / 100,
|
||||
}
|
||||
const chars = (proxy as unknown as { characters?: string }).characters
|
||||
if (proxy.type === 'TEXT' && chars) entry.text = chars
|
||||
if (visibleFill) entry.fill = colorHex(visibleFill.color)
|
||||
let cr = raw.cornerRadius as number
|
||||
if (raw.independentCorners) {
|
||||
const tl = raw.topLeftRadius as number ?? 0
|
||||
const tr = raw.topRightRadius as number ?? 0
|
||||
const br = raw.bottomRightRadius as number ?? 0
|
||||
const bl = raw.bottomLeftRadius as number ?? 0
|
||||
if (tl === tr && tr === br && br === bl) cr = tl
|
||||
else cr = Math.max(tl, tr, br, bl)
|
||||
}
|
||||
if (cr > 0) entry.cr = Math.round(cr * 10) / 10
|
||||
if (raw.clipsContent) entry.clip = true
|
||||
|
||||
ourNodes.set(path, entry)
|
||||
const children = (proxy as unknown as { children?: unknown[] }).children
|
||||
if (children) {
|
||||
const childNames = new Map<string, number>()
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
collect(children[i] as ReturnType<FigmaAPI['wrapNode']>, path, i, childNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pageChildren = (api.wrapNode(page.id) as unknown as { children?: unknown[] }).children ?? []
|
||||
const topNames = new Map<string, number>()
|
||||
for (let i = 0; i < pageChildren.length; i++) {
|
||||
collect(pageChildren[i] as ReturnType<FigmaAPI['wrapNode']>, '', i, topNames)
|
||||
}
|
||||
|
||||
let diffs = 0, visibility = 0, text = 0, fills = 0, radius = 0, size = 0, matched = 0
|
||||
|
||||
for (const [path, t] of truthMap) {
|
||||
const o = ourNodes.get(path)
|
||||
if (!o) continue
|
||||
matched++
|
||||
|
||||
if (o.visible !== t.visible) { visibility++; diffs++ }
|
||||
if (t.text && o.text !== t.text) { text++; diffs++ }
|
||||
if (t.fill && o.fill && t.fill.toLowerCase() !== o.fill.toLowerCase()) { fills++; diffs++ }
|
||||
if (t.cr && t.cr > 0) {
|
||||
const oCr = o.cr ?? 0
|
||||
// Both >= half the smaller dimension = fully rounded (pill), treat as equal
|
||||
const minDim = Math.min(t.width, t.height)
|
||||
const bothPill = oCr >= minDim / 2 && t.cr >= minDim / 2
|
||||
if (!bothPill && Math.abs(oCr - t.cr) > 1) { radius++; diffs++ }
|
||||
}
|
||||
if (t.visible && o.visible && (Math.abs(o.width - t.width) > 1 || Math.abs(o.height - t.height) > 1)) {
|
||||
size++; diffs++
|
||||
}
|
||||
}
|
||||
|
||||
const unmatched = truth.length - matched
|
||||
|
||||
console.log(`METRIC total_diffs=${diffs}`)
|
||||
console.log(`METRIC visibility=${visibility}`)
|
||||
console.log(`METRIC text=${text}`)
|
||||
console.log(`METRIC fills=${fills}`)
|
||||
console.log(`METRIC radius=${radius}`)
|
||||
console.log(`METRIC size=${size}`)
|
||||
console.log(`METRIC unmatched=${unmatched}`)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
bun run test:unit 2>&1 | tail -5
|
||||
bun run lint 2>&1 | tail -3
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Autoresearch Ideas
|
||||
|
||||
## High Priority (11 fill diffs)
|
||||
- **Library variable alias resolution**: ALL remaining fill diffs are caused by `colorVar` on symbolOverride paints. The `paint.color` contains the fallback value but the `colorVar.value.alias.assetRef` references an EXTERNAL library variable. Local copy exists but with different GUID. Need to build mapping from `assetRef.key + version` → local variable ID using `variableConsumptionMap` entries on NodeChanges. This would fix all 11 fill diffs at once.
|
||||
|
||||
## Medium Priority (50 size diffs)
|
||||
- **Badge area layout (~25 nodes)**: Badge INSTANCE width 96→85.32 is a Yoga layout issue. Auto-layout computes width from children sizes which depend on text measurement. Could improve by applying DSD size overrides to the Badge shell directly (if DSD entries exist but aren't resolving).
|
||||
- **Datepicker width (7 nodes)**: `_datepicker-date-range-link` width 32→131. FIXED sizing but Figma shows wider — check if `layoutGrow` or parent `FILL` sizing should expand it.
|
||||
- **Remaining Vector scaling (16 nodes)**: Inside Badge area. Would cascade-fix if Badge width is corrected.
|
||||
|
||||
## Attempted & Reverted
|
||||
- **kiwiPropertyNodes in seeds + preserveFills**: Adding kiwiPropertyNodes as BFS extra seeds allows propagation of kiwi NC values to clones, but the skip=seeds coupling causes visibility regressions. Need to decouple "BFS start nodes" from "don't overwrite" in propagateOverridesTransitively — a structural refactor of the sync function's skip logic.
|
||||
|
||||
## Investigated / Won't Fix In This Session
|
||||
- Avatar distortion: Fixed by skipping auto-layout instances in constraint scaling
|
||||
- 99 unmatched nodes: Fixed by name-based tree path matching
|
||||
- cornerRadius 999 vs 890: Fixed by pill-shape tolerance
|
||||
- Bold toolbar button fill: Fixed by self-referencing symbolOverride skip
|
||||
- Icon cropping: Fixed by DSD single-child fallback + SCALE constraints
|
||||
- Text measurement diffs (~10 nodes): Font-dependent, sub-2px differences
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{"type":"config","name":"Figma .fig import fidelity","metricName":"total_diffs","metricUnit":"","bestDirection":"lower"}
|
||||
{"run":1,"commit":"8a90594","metric":246,"metrics":{"visibility":1,"text":0,"fills":10,"radius":161,"size":74,"unmatched":99},"status":"keep","description":"Baseline: 246 diffs (1 vis, 0 text, 10 fills, 161 radius, 74 size, 99 unmatched)","timestamp":1773482910363,"segment":0}
|
||||
{"run":2,"commit":"3746d1a","metric":99,"metrics":{"visibility":1,"text":0,"fills":10,"radius":14,"size":74,"unmatched":99},"status":"keep","description":"Fix comparison: read individual corner radii when independentCorners=true","timestamp":1773483304162,"segment":0}
|
||||
{"run":3,"commit":"7fc3a64","metric":85,"metrics":{"visibility":1,"text":0,"fills":10,"radius":0,"size":74,"unmatched":99},"status":"keep","description":"Fix comparison: treat both-pill cornerRadius values as equal","timestamp":1773483389254,"segment":0}
|
||||
{"run":4,"commit":"2858d94","metric":84,"metrics":{"visibility":1,"text":0,"fills":9,"radius":0,"size":74,"unmatched":99},"status":"keep","description":"Skip self-referencing symbolOverrides on nodes with explicit kiwi properties (fixes bold toolbar button fill)","timestamp":1773484182748,"segment":0}
|
||||
{"run":5,"commit":"fd5b0b5","metric":106,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":94,"unmatched":0},"status":"keep","description":"Name-based tree path matching: 0 unmatched (was 99), reveals 106 total real diffs","timestamp":1773488934943,"segment":0}
|
||||
{"run":6,"commit":"97ba675","metric":70,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":58,"unmatched":0},"status":"keep","description":"Apply SCALE constraint resizing to instance children + propagate through clone chains","timestamp":1773489529498,"segment":0}
|
||||
{"run":7,"commit":"4f3df83","metric":62,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":50,"unmatched":0},"status":"keep","description":"Skip SCALE constraint resizing for auto-layout instances (fixes Avatar distortion)","timestamp":1773490511259,"segment":0}
|
||||
{"run":8,"commit":"919e4cf","metric":62,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":50,"unmatched":0},"status":"discard","description":"Analysis only: remaining 11 fills are all library variable aliases, 50 sizes are layout-dependent","timestamp":1773491467403,"segment":0}
|
||||
{"run":9,"commit":"4f3df83","metric":62,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":50,"unmatched":0},"status":"discard","description":"Reverted kiwiPropertyNodes/extraSeeds experiments — too complex, caused regressions","timestamp":1773493300107,"segment":0}
|
||||
{"run":10,"commit":"b6a1d91","metric":62,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":50,"unmatched":0},"status":"discard","description":"Reverted reapplyKiwiProperties — can't distinguish sync-overwritten from symbolOverride-set fills","timestamp":1773493747060,"segment":0}
|
||||
{"run":11,"commit":"d513312","metric":62,"metrics":{"visibility":1,"text":0,"fills":11,"radius":0,"size":50,"unmatched":0},"status":"keep","description":"Narrow kiwiPropertyNodes: only flag nodes whose fills/radius/visible DIFFER from component source","timestamp":1773494252125,"segment":0}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# Autoresearch: Figma .fig Import Fidelity
|
||||
|
||||
## Objective
|
||||
Minimize the number of node property differences between our .fig file import
|
||||
and the ground truth from Figma's Plugin API. The benchmark compares visibility,
|
||||
text content, fill colors, corner radius, and node dimensions on the Preview page
|
||||
of the Preline UI design system.
|
||||
|
||||
## Metrics
|
||||
- **Primary**: `total_diffs` (count, lower is better) — sum of all property mismatches
|
||||
- **Secondary**: `visibility`, `text`, `fills`, `radius`, `size`, `unmatched`
|
||||
|
||||
## How to Run
|
||||
```
|
||||
./autoresearch.sh
|
||||
```
|
||||
Outputs `METRIC name=number` lines. Uses `tests/fixtures/gold-preview.fig` as input
|
||||
and `tests/fixtures/gold-preview-truth.json` as ground truth (extracted from live Figma).
|
||||
|
||||
## Files in Scope
|
||||
- `packages/core/src/kiwi/instance-overrides/` — override resolution pipeline (types, index, populate, resolve, symbol-overrides, sync, props, dsd)
|
||||
- `packages/core/src/kiwi/kiwi-convert.ts` — main kiwi-to-SceneNode converter
|
||||
- `packages/core/src/kiwi/kiwi-convert-overrides.ts` — symbolOverride field conversion
|
||||
- `packages/core/src/kiwi/codec.ts` — NodeChange type definitions
|
||||
- `packages/core/src/scene-graph.ts` — SceneNode type and defaults
|
||||
- `packages/core/src/renderer/scene.ts` — render pipeline (clipping, effects)
|
||||
- `packages/core/src/renderer/shapes.ts` — makeRRect, shape helpers
|
||||
- `autoresearch-compare.ts` — comparison script (may refine matching logic)
|
||||
|
||||
## Off Limits
|
||||
- `packages/core/src/kiwi/kiwi-schema/` — vendored kiwi codec, do not modify
|
||||
- Test fixtures (.fig files) — read-only
|
||||
- Anything in `src/` (app code) — this is a core import fidelity issue
|
||||
|
||||
## Constraints
|
||||
- All 914+ engine tests must pass (`bun run test:unit`)
|
||||
- Zero lint errors (`bun run lint`)
|
||||
- No behavior regressions — fixes should be additive
|
||||
|
||||
## What's Been Tried
|
||||
- **Instance swap overrides**: Fixed propagation through clone chains (ef0b45f)
|
||||
- **Component property defaults**: Empty kiwi value `{}` → reset to initialValue (2cecdff)
|
||||
- **Text overrides clobbered by second sync**: Added `protect` set (72ec3ee)
|
||||
- **DSD for swapped instances**: Single-child fallback in resolveOverrideTarget (d93c475)
|
||||
- **Rounded clipping**: clipRRect when clipsContent + cornerRadius (49423e4)
|
||||
- **Shadow child shape**: Drop shadow on transparent containers follows first child (f70338d)
|
||||
- **Current best**: 62 total_diffs (1 vis, 0 text, 11 fills, 0 radius, 50 size, 0 unmatched) — 74.8% improvement
|
||||
- **kiwiPropertyNodes narrowed**: Only nodes whose fills/radius/visible actually differ from component source (was too broad — any NC with fillPaints)
|
||||
- **reapplyKiwiProperties failed**: Can't distinguish sync-overwritten from symbolOverride-set fills. Reverted.
|
||||
- **1 visibility diff root cause**: Email TEXT 0:3480 stays visible because its source 0:3455 IS in seeds (kiwiPropertyNodes) but 0:3452→0:3477 parent sync never runs (0:3477 gets visited by a different sync path first). Needs BFS/skip decoupling.
|
||||
- **Comparison fixes**: independentCorners radius reading, pill-shape tolerance, name-based tree matching
|
||||
- **kiwiPropertyNodes**: Nodes with explicit kiwi NC fills/cornerRadius are added to seeds AND protected from sync overwrite
|
||||
- **Self-referencing symbolOverride**: When an override resolves to the instance itself, skip if the instance has explicit kiwi NC properties
|
||||
- **SCALE constraint resizing**: Apply proportional scaling to children when instance size ≠ component size; skip auto-layout instances; propagate through clone chains
|
||||
- **Remaining fills (11)**: Variable-bound colors (3 Indicators, 1 Ellipse), deep chain color overrides (3 badge Placeholders, 1 Link, 1 Vector, 1 button Placeholder, 1 Left Divider)
|
||||
- **Remaining sizes (50)**: 16 Vectors (Badge 1.12x ratio), 7 datepicker widths, 3+3+3 Badge/Avatar/Placeholder sizing (layout-dependent), 4 Groups, misc text widths
|
||||
- **Root causes of remaining diffs**:
|
||||
- **Fills (11)**: ALL fill overrides use `colorVar` library aliases. The `paint.color` is the fallback value, but the alias resolves to a different color in context. Need library asset ref → local variable mapping via `variableConsumptionMap`.
|
||||
- **Sizes (50)**: 30+ are Badge area (~1.12x ratio from layout-dependent width), 7 datepicker (layout), rest are text measurement diffs. These need pixel-perfect text measurement or DSD propagation improvements.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Compare gold-preview.fig import against Figma Plugin API ground truth.
|
||||
# Outputs METRIC lines for the autoresearch dashboard.
|
||||
|
||||
FIXTURE=tests/fixtures/gold-preview.fig
|
||||
|
||||
# Run the comparison script
|
||||
bun run autoresearch-compare.ts "$FIXTURE" 2>/dev/null
|
||||
Loading…
Reference in a new issue