mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 17:30:50 +00:00
* feat(core): add figma motion easing mapping * feat(core): translate figma motion doc to gsap timeline spec * feat(core): emit paused GSAP timeline script from figma motion spec * fix(core): restore type exports dropped from figma barrel in Task 8 * feat(skills): add /figma import skill + catalog wiring Add the agent-facing /figma skill (asset + Figma Motion import via the Figma MCP connector, built on @hyperframes/core/figma) and wire it into the skill catalog across CLAUDE.md, README.md, docs/guides/skills.mdx, and the hyperframes router's capability map. Bumps the skill count from 19 to 20 in CLAUDE.md and README.md. * fix(core): use replaceAll for figma node-id dash-to-colon conversion * style: format skills catalog tables oxfmt-align the README and router SKILL.md tables after the /figma + /hyperframes-keyframes merge left uneven column padding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): add missing cache fields to telemetry test fixture ExtractionPhaseBreakdown gained cachePublishFailures/cacheGcEvictions/ cacheGcBytesFreed/cacheAgedPartialsCleared; the studioRenderTelemetry test fixture was never updated, breaking Typecheck on main and every PR based on it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
import type { FigmaRef } from "./types";
|
|
|
|
const FILE_KEY_RE = /\/(?:design|file|proto)\/([A-Za-z0-9]+)/;
|
|
|
|
function normalizeNodeId(raw: string): string {
|
|
return raw.replaceAll("-", ":");
|
|
}
|
|
|
|
export function parseFigmaRef(input: string): FigmaRef {
|
|
const trimmed = input.trim();
|
|
if (trimmed.length === 0) throw new Error("parseFigmaRef: empty input");
|
|
|
|
if (!trimmed.includes("/")) {
|
|
const colon = trimmed.indexOf(":");
|
|
if (colon === -1) return { fileKey: trimmed };
|
|
const fileKey = trimmed.slice(0, colon);
|
|
const node = trimmed.slice(colon + 1);
|
|
if (fileKey.length === 0) throw new Error(`parseFigmaRef: invalid ref "${input}"`);
|
|
return node.length > 0 ? { fileKey, nodeId: normalizeNodeId(node) } : { fileKey };
|
|
}
|
|
|
|
const keyMatch = trimmed.match(FILE_KEY_RE);
|
|
const fileKey = keyMatch?.[1];
|
|
if (fileKey === undefined) throw new Error(`parseFigmaRef: no fileKey in "${input}"`);
|
|
|
|
const q = trimmed.indexOf("?");
|
|
if (q !== -1) {
|
|
const raw = new URLSearchParams(trimmed.slice(q + 1)).get("node-id");
|
|
if (raw !== null && raw.length > 0) return { fileKey, nodeId: normalizeNodeId(raw) };
|
|
}
|
|
return { fileKey };
|
|
}
|