Commit Graph
171 Commits
Author SHA1 Message Date
Miguel Ángel 7be4f92f18 chore: release v0.6.52 2026-05-27 20:23:26 -04:00
Miguel Ángel d8ce2e4b50 chore: release v0.6.51 2026-05-27 12:15:38 -04:00
Miguel Ángel 5cd4db07e3 chore: release v0.6.50 2026-05-27 15:28:56 +00:00
Miguel Ángel 7ea4d1c131 chore: release v0.6.49 2026-05-27 01:45:45 -04:00
Miguel Ángel 7cde0d9554 chore: release v0.6.48 2026-05-26 23:46:36 -04:00
Miguel Ángel 2d0acb3494 chore: release v0.6.47 2026-05-26 20:26:05 -04:00
Miguel Ángel 60cb9552e4 chore: release v0.6.46 2026-05-25 23:49:33 +00:00
Miguel Ángel 9a4a00582c chore: release v0.6.45 2026-05-25 19:45:52 +00:00
Miguel Ángel c46adb52e2 chore: release v0.6.44 2026-05-25 16:56:50 +00:00
Miguel ÁngelandClaude Sonnet 4.6 e4e2234303 chore: release v0.6.43
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:35:55 +00:00
Miguel Ángel d2b915be9e chore: release v0.6.42 2026-05-24 17:36:30 -04:00
Miguel Ángel 7461f1df30 chore: bump version to 0.6.41 2026-05-24 14:26:59 -04:00
Miguel Ángel 47d57bff10 chore: bump version to 0.6.40 2026-05-23 15:01:38 -04:00
James 179b09ec9d chore: release v0.6.39 2026-05-23 17:30:05 +00:00
Miguel Ángel 3560678bb2 chore: bump version to 0.6.38 2026-05-23 00:13:50 -04:00
James 258bd6256c chore: release v0.6.37 2026-05-22 23:17:08 +00:00
Miguel Ángel 154359d95d chore: bump version to 0.6.36 2026-05-22 13:37:28 -04:00
Miguel Ángel 6c191e2292 chore: bump version to 0.6.35 2026-05-22 13:31:45 -04:00
Miguel Ángel aebb7b2660 chore: bump version to 0.6.34 2026-05-22 11:44:06 -04:00
Miguel Ángel ee4e088434 chore: bump version to 0.6.33 2026-05-21 22:03:20 -04:00
Miguel Ángel 13be10afb7 chore: bump version to 0.6.32 2026-05-21 18:29:54 -04:00
ukimsanov 65c5209be8 chore(cli): bump sharp ^0.34.0 → ^0.34.5
Required by the contact-sheet pagination code added on this PR
(uses Sharp APIs that landed in 0.34.5). Originally bumped on
#987 by mistake — moved here per Copilot review.
2026-05-21 10:57:37 -07:00
Ular Kimsanov 12808fd38f Merge pull request #987 from heygen-com/feat/capture-font-extractor
feat(capture): identify hashed fonts via OpenType name table
2026-05-21 10:09:25 -07:00
Miguel Ángel 90a4e4b1c5 chore: bump version to 0.6.31 2026-05-21 12:22:12 -04:00
Miguel Ángel 114b83bbf6 chore: bump version to 0.6.30 2026-05-21 00:05:27 -04:00
ukimsanov 5e7a7a8956 fix(capture): address review feedback on font extractor
Five fixes from Copilot's inline review + Miguel's note on PR #987:

1. inferWeightFromSubfamily — only matched concatenated forms
   ("extralight", "semibold"). Spaced ("Extra Light") and
   hyphenated ("Extra-Light") variants fell through to the 400
   default, misreporting 200-weight fonts as 400. Now normalizes
   `[\s-]+` out of the subfamily before matching.

2. meta.tool — was hardcoded to "fontkit@2.0.4" but
   `packages/cli/package.json` allows ^2.0.4, so the manifest
   string would drift on every dep bump. Now records just
   "fontkit"; the version moves with the dep and can be discovered
   from package.json at debug-time if needed.

3. FontFileMetadata.rawFamily — docstring said "nameID 16 preferred,
   then nameID 1" but the code also derives from PostScript via
   deriveFamilyFromPostscript when both name-table fields are
   missing. Doc now reflects the actual three-step precedence.

4. FontFileMetadata.weight — docstring said "100-900" but the code
   emits 0 (when identified: false) and 950 (when
   canonicalizeFamily picks ExtraBlack/UltraBlack). Doc now
   documents both edge values explicitly.

5. sharp ^0.34.5 — bumped from ^0.34.0 on this PR but font
   extraction doesn't use sharp; the bump is needed by the contact
   sheet code in PR #988. Reverted on #987; will re-bump on #988
   where it's actually consumed.

Also adds vitest coverage:
- 34 tests in fontMetadataExtractor.test.ts
- Covers inferWeightFromSubfamily for concatenated, spaced, and
  hyphenated forms (including composite styles like "Bold Italic"
  and case-insensitivity)
- Covers canonicalizeFamily for unchanged families, stripped
  weight tokens, preserved width modifiers, and the 950 emit
- Integration tests for extractFontMetadata (non-existent dir,
  empty dir) verifying the meta.tool / generatedAt shape

Exported `inferWeightFromSubfamily` and `canonicalizeFamily` for
testing. Pure functions, internal helpers, but exporting is the
clean way to pin their behavior against regressions.
2026-05-20 17:54:32 -07:00
ukimsanov db94b505dd feat(capture): identify hashed fonts via OpenType name table
Modern frameworks (Next.js, Webpack) hash font filenames like
`f9b8e1e8d4c3f0a7-s.woff2`, so the capture pipeline can't tell which
file belongs to which family by reading the filename. Sub-agents
authoring DESIGN.md were guessing or falling back to system fonts.

This adds `fontMetadataExtractor.ts`: reads the binary OpenType `name`
table via `fontkit`, identifies each downloaded font by its real
family name, and writes `capture/extracted/fonts-manifest.json` with
per-file metadata + per-family aggregates (weights, variable-font
axes, file counts).

- Canonicalizes static-weight family-name packaging: "Inter Medium"
  resolves to family "Inter" with weight 500, "Semi Bold" normalizes
  to "SemiBold", etc. Width modifiers ("Tight", "Condensed") are NOT
  stripped — they denote separate typographic families.
- Reads variable-font axes from `fvar` so a single .woff2 carrying a
  full weight range is identified as variable (e.g. "Inter (100-900
  variable)").
- Uses `@types/fontkit` properly (no `unknown` cast), with a
  Font/FontCollection type guard. fontkit API drift surfaces as a
  compile error rather than silent undefined.
- Wired into `capture/index.ts` after `downloadAndRewriteFonts` so it
  runs after fonts are already on disk. Non-fatal try/catch — capture
  succeeds even if extraction fails.

Tested against 9 captures: 132/132 fonts identified by real family
name, including hashed Next.js builds.
2026-05-20 13:55:07 -07:00
Miguel Ángel d64de2b84d chore: release v0.6.29 2026-05-20 07:21:20 +00:00
Miguel Ángel 0decb88946 chore: release v0.6.28 2026-05-19 19:38:08 -04:00
Miguel Ángel 4237165517 chore: release v0.6.27 2026-05-19 17:12:32 -04:00
James f4e96a58ed chore: release v0.6.26 2026-05-19 18:16:54 +00:00
James 2dc2531cf7 chore: release v0.6.25 2026-05-19 02:33:31 +00:00
Miguel Ángel 7354d61371 chore: release v0.6.24 2026-05-18 22:00:56 -04:00
Miguel Ángel ffbc18ad31 feat(studio): full Blocks panel — browse, search, add, drag-and-drop registry items
Adds a Blocks tab to the Studio left sidebar with the full 78-item registry
catalog (58 blocks + 20 components). Users can browse by category, search by
title/description, preview CDN-hosted poster thumbnails with video-on-hover,
and install items on-demand with one click or drag-to-timeline.

Core changes:
- BlockCategory type + resolveBlockCategory() for 7 categories (Captions, VFX,
  Transitions, Effects, Social, Data, Scenes)
- Registry API routes: GET /api/registry/blocks (catalog) + POST install
- StudioApiAdapter extended with listRegistryCatalog + installRegistryBlock
- Vite adapter reads from disk; CLI adapter fetches from GitHub (24h cache)
- BlockParam interface + params on 6 blocks for future parameter controls

Studio UI:
- 4th sidebar tab "Blocks" with responsive grid, category pills, search bar
- BlockCard: CDN poster thumbnail, video autoplay on hover, duration + WebGL badges
- On-demand install: blocks append as sub-compositions on timeline; components
  overlay at start=0 spanning full duration with transparent background patching
- TIMELINE_BLOCK_MIME drag-and-drop to timeline
- BlockParamsPanel (Phase 3 scaffold) auto-opens for parameterized blocks

Registry manifests:
- All 58 blocks backfilled with preview: { video, poster } CDN URLs
- All 20 components normalized to object format + poster URLs added
- 6 blocks annotated with params (Liquid Glass/Background, Portal, Chart,
  Logo Outro, Magnetic)
- flowchart-vertical preview generated and uploaded to CDN
2026-05-18 21:15:15 -04:00
James 2087d5dab2 chore: add fallow config and fix high-signal findings
Configure fallow via .fallowrc.jsonc so its analysis reflects this repo's
real entry surface, then fix the genuine issues it found.

Fallow noise reduction (601 → 276 dead-code findings):
- Ignore docs/, test fixtures, skill test-corpora, registry/, examples/
- Declare worker entry points loaded dynamically by file path
  (pngDecodeBlitWorker.ts, shaderTransitionWorker.ts)
- Declare runtime IIFE entry (core/src/runtime/entry.ts) built outside the
  import graph by build-hyperframes-runtime-artifact.ts
- Declare bun:test files in producer + aws-lambda as test entries
- Ignore dynamically-resolved deps: tsup external (puppeteer-core, esbuild,
  giget), peer/static-file (gsap in player perf tests), workspace deps
  hoisted by bun (happy-dom, @hyperframes/*), and @fontsource/* packages
  read via readFileSync in generate-font-data.ts

Extract inline build:fonts scripts:
- packages/{cli,producer}/package.json had multi-line `node -e ...` blobs
  containing braces that fallow mis-parsed as glob alternate groups. Moved
  to dedicated build-fonts.mjs scripts.

Fix duplicate exports:
- Remove dead FileIcon alias in studio/SystemIcons.tsx (FileTreeIcons.tsx
  has the real, used one)
- Consolidate ValidationResult: drop the identical duplicate in
  gsapParser.ts; both parsers now import from core.types
- Suppress intentional namespace patterns (per-namespace ML manager
  exports; CLI per-command 'examples' convention; fileServer.ts test-only
  isPathInside which has different symlink semantics from utils/paths.ts)

Break circular dep (studio/components/editor):
- manualEditsDom.ts re-exported clearStudioPathOffset / clearStudioRotation
  / clearStudioBoxSize from manualEditsSnapshot.ts, which imports four
  helpers from manualEditsDom.ts — back-edge cycle
- Re-export moved to manualEdits.ts (the package-public barrel) where the
  rest of the snapshot re-exports already live; underlying files now form
  a clean DAG

Remove genuinely unused deps:
- studio: motion (no imports anywhere), codemirror (umbrella package; the
  @codemirror/* sub-packages are used directly)
- cli: mime-types (plus its only consumer src/utils/mime.ts, which was a
  hardcoded mime table that didn't use the package), and its now-stale
  tsup external entry

Verified: typecheck across core/cli/producer/studio is clean, oxlint
+ oxfmt pass, manualEdits.test.ts (18 tests) and core parser tests (69
tests) still pass.

Deferred follow-ups (real findings, separate PRs):
- 8 circular deps in producer/services/render/stages/ — renderOrchestrator
  ↔ captureHdr* / captureStage / extractVideosStage form a hub cycle
- ~14 unused files in producer/src/services/ that look like dead
  re-export shims to @hyperframes/engine, but aren't in the public
  exports map — need to confirm no deep-import consumers before deletion
- waveform.ts complexity hotspot
2026-05-18 18:57:21 +00:00
Miguel Ángel 27efcd0f80 chore: release v0.6.22 2026-05-18 14:23:43 -04:00
Miguel Ángel 8163f38077 chore: release v0.6.21 2026-05-18 11:29:01 -04:00
Miguel Ángel 6c533c0b0f chore: release v0.6.20 2026-05-17 18:07:59 -04:00
Miguel Ángel 04aa6a644f chore: release v0.6.19 2026-05-17 17:55:32 -04:00
James Russo e90ad2da61 feat(cli): add hyperframes lambda deploy/render/progress/destroy (#910)
* feat(cli): add hyperframes lambda deploy/render/progress/destroy

Wraps the @hyperframes/aws-lambda SDK + the Phase 6a SAM template behind
a single CLI surface so an end-to-end render is three commands instead
of the ~8 manual bun+sam+aws steps the smoke script does today:

  hyperframes lambda deploy
  hyperframes lambda render ./my-project --width 1920 --height 1080 --wait
  hyperframes lambda destroy

Subcommands:
  - deploy:        build handler.zip + sam-deploy + persist stack outputs
                   to <cwd>/.hyperframes/lambda-stack-<name>.json
  - sites create:  pre-upload a project to S3 with a stable content hash
                   so re-renders skip the tar+PUT pass
  - render:        start a Step Functions execution; --wait blocks and
                   streams per-chunk progress + accrued cost
  - progress:      one-shot snapshot — status, frames, cost breakdown,
                   errors. Accepts renderId or executionArn
  - destroy:       sam-delete + drop the local state file (S3 bucket
                   is Retain'd by the template; documented in --help
                   and in docs/packages/cli.mdx)

To keep @sparticuz/chromium out of the CLI's transitive deps, this also
adds a dedicated ./sdk subpath export to @hyperframes/aws-lambda; the
CLI imports from @hyperframes/aws-lambda/sdk exclusively. The existing
. barrel still re-exports both handler + SDK for adopters who want one
entry point.

Defaults are deliberately cost-conservative for first-time users:
--concurrency=8 (low enough to never surprise) and --memory=10240 (the
common case; documented for adopters who want to tune down).

Tests: 5 unit tests on the state-file round-trip. CLI integration
against sam local invoke is part of the upcoming PR 6.6 (lambda-local
regression harness).

* refactor(cli): /simplify pass on the lambda command group

Two small cleanups on top of the lambda CLI:

  - Replace parseFormat / parseCodec / parseQuality / parseChromeSource
    (four near-identical helpers) with a single generic parseEnum() +
    typed const-tuple lookups. The four callers now read as one-line
    arrow functions that lift the allowed values out of the function
    body so they're easy to extend.

  - DEFAULT_STACK_NAME was const-declared then re-exported at the
    bottom of state.ts; just mark the const export inline.

No behavior changes. All CLI tests still pass.

* fix(cli): keep @hyperframes/aws-lambda external in the tsup bundle

esbuild can't bundle @hyperframes/aws-lambda's transitive AWS SDK
deps (@aws-sdk/* + @smithy/*) cleanly into a node binary — the
SDK's .browser.js conditional re-exports break the resolver:

  ESM Build failed
    No matching export in "splitStream.browser.js" for import
    "splitStream" (and ~10 similar errors)

Mark aws-lambda as `external` so esbuild doesn't follow it, and
move it from devDependencies to dependencies so the published CLI
can resolve it from node_modules at runtime. The lambda subverb
files dynamic-import only on `hyperframes lambda *` invocation, so
the CLI cold-start cost is unchanged.

The install-size hit (AWS SDK + @sparticuz/chromium ≈ 200 MiB) is
documented as a v1 tradeoff; a future split into a lambda-sdk-only
subpackage can pare this back.

* fix(cli): address PR review on lambda CLI

Two blockers + four important items from Vai's review:

  - `--memory` was parsed and recorded in the local state file but
    never forwarded to `sam deploy` as a parameter override. Worse,
    `progress.ts` then read the *recorded* value for cost math, so
    `--memory 5120` produced wrong cost numbers downstream. Thread
    `LambdaMemoryMb` through samDeploy's --parameter-overrides.

  - `--profile` was only consumed by deploy / destroy. render and
    progress fell back to the default credentials chain — a user
    with `--profile prod` would silently render against their
    default account (wrong-account billing footgun). Set
    `process.env.AWS_PROFILE` (and `AWS_REGION`) in the dispatcher
    before any subverb runs; the AWS SDK reads them natively, so
    render / progress / sites all benefit without each subverb
    threading the flag through the SDK call.

  - `--profile` + destroy now also reads `process.env.AWS_PROFILE`
    as a fallback (matching deploy's existing env fallback).

  - `--wait --json` printed both the start handle AND the final
    progress snapshot, producing two concatenated JSON blobs that
    `jq` rejected. Now emits a single document: handle (without
    --wait) OR final progress (with --wait).

  - Negative integers on `--width` / `--height` / `--chunk-size` /
    `--max-parallel-chunks` / `--memory` / `--concurrency` now fail
    loudly via a new `parsePositiveInt` wrapper instead of flowing
    into the SDK and producing opaque AWS validation errors mid-
    render.

  - `DEFAULT_STACK_NAME` is now centralized to the literal
    `"hyperframes-default"` and consumed from one place. Previously
    the value was assembled as `hyperframes-${"default"}` in three
    sites and hardcoded as `"hyperframes-default"` in a fourth.
    `requireStack`'s hint now matches the dispatcher's default.

The faked `SiteHandle` for `--site-id` keeps the documented
placeholder fields but also surfaces `bucketName` (from PR 909's
extended SiteHandle interface), matching the SDK contract.

All CLI unit tests + the full bundler build still pass.

* fix(cli): keep aws-lambda out of CLI runtime deps

The "Smoke: global install" CI step packs the CLI via `npm pack` and
installs it globally via `npm install -g <tgz>`. npm doesn't understand
the workspace: protocol, so a runtime `dependencies` entry of
`@hyperframes/aws-lambda: workspace:*` blows up with:

  npm error code EUNSUPPORTEDPROTOCOL
  npm error Unsupported URL Type "workspace:": workspace:*

(pnpm rewrites workspace:* on publish; npm pack doesn't.)

Three changes to unblock the smoke + keep the published CLI install
small for users who don't deploy to Lambda:

  - Move `@hyperframes/aws-lambda` from CLI's `dependencies` back to
    `devDependencies`. It's already external in tsup.config.ts; the
    bundle references it via runtime resolution only.

  - Convert the static `import { … } from "@hyperframes/aws-lambda/sdk"`
    in sites.ts / render.ts / progress.ts to `await import()` inside
    each function. tsup with `splitting: false` was inlining those
    static imports at the top of the bundle, which made Node eagerly
    resolve them at CLI startup (MODULE_NOT_FOUND before any lambda
    subcommand even runs). Dynamic imports stay dynamic in the bundle.

  - Add a friendly missing-module check in the lambda dispatcher.
    When a user runs `hyperframes lambda deploy / render / sites /
    progress / destroy` without aws-lambda installed, they now see:

      @hyperframes/aws-lambda is not installed.
      The `hyperframes lambda deploy` command needs it at runtime.
      Install it alongside the CLI:
        npm install -g @hyperframes/aws-lambda

Verified locally: pack + global install + `hyperframes init --example
blank` now succeeds end-to-end (was the same scenario the CI smoke job
runs).
2026-05-17 13:06:00 -04:00
Miguel ÁngelandClaude Sonnet 4.6 78fce8bd8a chore: release v0.6.18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 15:32:53 +00:00
Miguel ÁngelandClaude Sonnet 4.6 3f976d454c chore: release v0.6.17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 15:15:54 +00:00
James b30fd29695 chore: release v0.6.16 2026-05-17 08:20:08 +00:00
James 5f8391bd96 chore: release v0.6.15 2026-05-16 23:30:38 +00:00
Miguel Ángel 1fca35b625 chore: release v0.6.14 2026-05-16 13:02:23 -07:00
Miguel ÁngelandClaude Sonnet 4.6 883260aae3 chore: release v0.6.13
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:35:46 +00:00
Miguel Ángel 2355d505e1 chore: release v0.6.12 2026-05-16 00:48:57 -07:00
Miguel Ángel 4212a28312 chore: release v0.6.11 2026-05-15 23:43:21 -07:00
Miguel Ángel d3c32a0b4d chore: release v0.6.10 2026-05-15 21:25:26 -07:00
Miguel Ángel 82c9b6b5ea chore: release v0.6.9 2026-05-15 19:45:01 -07:00