import { defineCommand } from "citty"; import { existsSync, readFileSync, statSync } from "node:fs"; import { resolve, dirname, basename, join, relative, sep } from "node:path"; import { parseGsapScript, type GsapAnimation } from "@hyperframes/core/gsap-parser"; import type { Example } from "./_examples.js"; import { c } from "../ui/colors.js"; import { ensureDOMParser } from "../utils/dom.js"; import { resolveProject } from "../utils/project.js"; import { withMeta } from "../utils/updateCheck.js"; export const examples: Example[] = [ ["Surface every keyframe + motion path in the project", "hyperframes keyframes"], ["Inspect one composition file", "hyperframes keyframes compositions/scene.html"], ["Machine-readable output for an agent", "hyperframes keyframes --json"], ["Only one element's keyframes", "hyperframes keyframes --selector '#puck-a'"], ["Runtime-aware hint for CSS/Anime compositions", "hyperframes keyframes --runtime all"], ]; // ── Surfaced shapes ────────────────────────────────────────────────────────── interface KeyframePoint { /** Tween-relative percentage (0–100). */ pct: number; /** Absolute timeline time (seconds) = tweenStart + pct/100 * duration. */ time: number; properties: Record; } interface SurfacedTween { id: string; target: string; method: string; group?: string; start: number; duration: number; end: number; /** "keyframes" (array/object form), "flat" (to/from), or "motionPath". */ shape: "keyframes" | "flat" | "motionPath"; keyframes: KeyframePoint[]; /** x/y position points (gsap offsets) when this tween animates position. */ path: Array<{ x: number; y: number }> | null; /** Animated ANCESTOR elements (nested composition): this element's rendered * motion is composed with theirs. Surfaced so a reader of the text/JSON * doesn't miss a parent's path/trajectory that lives on another element. */ composedWith?: Array<{ selector: string; summary: string }>; } /** One drawn stroke of a multi-stroke trace — a single position tween. */ interface TraceStroke { id: string; start: number; end: number; keyframes: KeyframePoint[]; points: Array<{ x: number; y: number }>; } /** An element's position motion composited into ordered strokes. The gaps * between strokes are pen-up jumps (a 0-duration `set`, or a discontinuity) * and are NOT drawn — this is how one element traces shapes with holes or * detached parts (a `?` dot, an icon counter, multi-letter words). */ interface SurfacedTrace { target: string; strokes: TraceStroke[]; } interface CssKeyframeStop { selector: string; declarations: string[]; } interface SurfacedCssKeyframes { name: string; selectors: string[]; keyframes: CssKeyframeStop[]; } interface SurfacedAnimeAnimation { kind: "animation" | "timeline"; targets: string[]; durations: Array; registered: boolean; } interface SurfacedComposition { composition: string; source: string; tweens: SurfacedTween[]; /** Multi-stroke traces: targets with ≥2 drawn position strokes, composited. */ traces: SurfacedTrace[]; cssKeyframes: SurfacedCssKeyframes[]; anime: SurfacedAnimeAnimation[]; } // ── GSAP extraction ────────────────────────────────────────────────────────── //