mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
fix(core,cli): parent-relative figma child geometry; groups stop rejecting subcommand flags
Both found running the brand-loop guide end-to-end against the Simple Design System: - nodeToHtml subtracted the ROOT origin from every node's absolute bounds, but CSS absolute positioning resolves against the nearest positioned ancestor — every nesting level re-added its ancestors' offsets, drifting nested content down-right and pushing deep children off-frame (hero buttons invisible, pricing grid collapsed to one card). Children now subtract their PARENT's box; regression test with a two-level tree. - trackCommandFailures asserted unknown flags against the command group's own (flagless) arg table even when the group was delegating to a subcommand, so `figma component <ref> --name x` imported and THEN threw "Unknown flag: --name". The assertion is now skipped when the first positional names a subcommand; leaf and non-delegating behavior is unchanged and covered by tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
31f0810be8
commit
dfe63af3ae
@@ -186,7 +186,7 @@ function unresolvedAttr(node: FigmaNodeDocument, ctx: RenderContext): string {
|
||||
return ` data-figma-unresolved="${escapeHtml(props.join(" "))}"`;
|
||||
}
|
||||
|
||||
function geometryCss(node: FigmaNodeDocument, ctx: RenderContext, isRoot: boolean): string[] {
|
||||
function geometryCss(node: FigmaNodeDocument, parentBox: Box, isRoot: boolean): string[] {
|
||||
const box = boxOf(node);
|
||||
const styles: string[] = [];
|
||||
if (!box) return styles;
|
||||
@@ -197,10 +197,14 @@ function geometryCss(node: FigmaNodeDocument, ctx: RenderContext, isRoot: boolea
|
||||
`height: ${round(box.height)}px`,
|
||||
);
|
||||
} else {
|
||||
// CSS absolute positioning is relative to the nearest positioned
|
||||
// ancestor — the PARENT's box, not the root origin. Subtracting the root
|
||||
// for every depth double-offsets nested children (each level re-adds its
|
||||
// ancestors' offsets), drifting content down-right and off-frame.
|
||||
styles.push(
|
||||
"position: absolute",
|
||||
`left: ${round(box.x - ctx.origin.x)}px`,
|
||||
`top: ${round(box.y - ctx.origin.y)}px`,
|
||||
`left: ${round(box.x - parentBox.x)}px`,
|
||||
`top: ${round(box.y - parentBox.y)}px`,
|
||||
`width: ${round(box.width)}px`,
|
||||
`height: ${round(box.height)}px`,
|
||||
);
|
||||
@@ -243,10 +247,15 @@ function decorationCss(node: FigmaNodeDocument, ctx: RenderContext): string[] {
|
||||
// instead of a RangeError; real figma frames are nowhere near this deep.
|
||||
const MAX_DEPTH = 500;
|
||||
|
||||
function renderChildren(node: FigmaNodeDocument, ctx: RenderContext, depth: number): string {
|
||||
function renderChildren(
|
||||
node: FigmaNodeDocument,
|
||||
ctx: RenderContext,
|
||||
depth: number,
|
||||
parentBox: Box,
|
||||
): string {
|
||||
const childHtml: string[] = [];
|
||||
for (const child of childDocuments(node)) {
|
||||
const rendered = renderNodeHtml(child, ctx, false, depth + 1);
|
||||
const rendered = renderNodeHtml(child, ctx, false, depth + 1, parentBox);
|
||||
if (rendered.length > 0) childHtml.push(rendered);
|
||||
}
|
||||
return childHtml.length > 0 ? `\n${childHtml.join("\n")}\n` : "";
|
||||
@@ -257,11 +266,12 @@ function renderNodeHtml(
|
||||
ctx: RenderContext,
|
||||
isRoot: boolean,
|
||||
depth = 0,
|
||||
parentBox: Box = ctx.origin,
|
||||
): string {
|
||||
if (node.visible === false || depth > MAX_DEPTH) return "";
|
||||
const slug = uniqueSlug(ctx, node.name);
|
||||
const style = escapeHtml(
|
||||
[...geometryCss(node, ctx, isRoot), ...decorationCss(node, ctx)].join("; "),
|
||||
[...geometryCss(node, parentBox, isRoot), ...decorationCss(node, ctx)].join("; "),
|
||||
);
|
||||
// data-hf-snippet marks the file as a mountable fragment, not a standalone
|
||||
// composition — the project linter skips composition-root rules for it.
|
||||
@@ -278,7 +288,7 @@ function renderNodeHtml(
|
||||
return `<div ${idAttrs} style="${style}">${text}</div>`;
|
||||
}
|
||||
|
||||
return `<div ${idAttrs} style="${style}">${renderChildren(node, ctx, depth)}</div>`;
|
||||
return `<div ${idAttrs} style="${style}">${renderChildren(node, ctx, depth, boxOf(node) ?? parentBox)}</div>`;
|
||||
}
|
||||
|
||||
export interface NodeToHtmlOptions {
|
||||
|
||||
Reference in New Issue
Block a user