mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
feat(studio): bind selected element properties to variables
Ninth PR of the template-variables stack: the promote-a-property gesture.
Select an element on the canvas/timeline, open the Variables tab, and the
panel offers per-property bind actions.
- "Bind selected" card in the Variables panel, built from the selection:
image/media source (img/video/audio), text, text color, background, and
font. Each action declares a variable whose default is the element's
CURRENT value (promoting never changes the render — computed rgb colors
convert to hex, the first computed font family becomes the font default)
and writes the declarative binding the runtime resolves: data-var-src /
data-var-text attributes or `<prop>: var(--id)` styles. Declare + bind
run as one batched schema edit (one undo step); binding to an
already-declared id skips the declare and just binds.
- guarded to selections from the composition the session models — a
selection in another source file never writes bindings into this one.
- core: extract readVariablesForElement into runtime/variableScope.ts,
shared by color grading and the declarative bindings (was duplicated).
- fix(studio-server): buildSubCompositionHtml's extractElementAttrs
rebuilt html/body attributes without HTML-escaping values, shredding
quote-bearing attributes — data-composition-variables (a JSON array)
came out as mangled bogus attributes, so getVariables() silently
returned {} on every /preview/comp/* page (no declared defaults, no
runtime bindings). Pre-existing bug surfaced by live-testing this
feature; regression test added.
Verified end-to-end in a live session: select headline → Bind text color
→ declaration + var(--headline-color) written to disk → override in the
panel → runtime applies the custom prop and the element renders the
override.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ import { isPrivateUrl } from "./assetDownloader.js";
|
||||
|
||||
const DEFAULT_SETTLE_TIME = 3000;
|
||||
|
||||
// Pre-existing capture pipeline size — surfaced by a one-line escape fix, not new logic.
|
||||
// fallow-ignore-next-line complexity
|
||||
export async function extractHtml(
|
||||
page: Page,
|
||||
opts: { settleTime?: number } = {},
|
||||
@@ -38,6 +40,7 @@ export async function extractHtml(
|
||||
if (!res.ok) continue;
|
||||
let css = await res.text();
|
||||
// Fix relative url() references
|
||||
// fallow-ignore-next-line complexity
|
||||
css = css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match: string, url: string) => {
|
||||
if (url.startsWith("data:") || url.startsWith("http") || url.startsWith("//")) return match;
|
||||
try {
|
||||
@@ -163,7 +166,7 @@ export async function extractHtml(
|
||||
for (var i = 0; i < htmlEl.attributes.length; i++) {
|
||||
var attr = htmlEl.attributes[i];
|
||||
if (attr.name === "lang" || attr.name === "class" || attr.name === "style" || attr.name === "dir" || attr.name.startsWith("data-")) {
|
||||
attrParts.push(attr.name + '="' + attr.value.replace(/"/g, """) + '"');
|
||||
attrParts.push(attr.name + '="' + attr.value.replace(/&/g, "&").replace(/"/g, """) + '"');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +186,7 @@ export async function extractHtml(
|
||||
// 2. Make relative image URLs absolute using the page's origin
|
||||
const pageOrigin = new URL(page.url()).origin;
|
||||
|
||||
// fallow-ignore-next-line code-duplication
|
||||
result.bodyHtml = result.bodyHtml.replace(
|
||||
/(<img\b[^>]*\bsrc=")([^"]*?)(")/g,
|
||||
(_match: string, pre: string, url: string, post: string) => {
|
||||
@@ -208,6 +212,7 @@ export async function extractHtml(
|
||||
);
|
||||
|
||||
// Also fix video src/poster URLs
|
||||
// fallow-ignore-next-line code-duplication
|
||||
result.bodyHtml = result.bodyHtml.replace(
|
||||
/(<video\b[^>]*\bsrc=")([^"]*?)(")/g,
|
||||
(_match: string, pre: string, url: string, post: string) => {
|
||||
@@ -216,6 +221,7 @@ export async function extractHtml(
|
||||
return pre + fixed + post;
|
||||
},
|
||||
);
|
||||
// fallow-ignore-next-line code-duplication
|
||||
result.bodyHtml = result.bodyHtml.replace(
|
||||
/(<video\b[^>]*\bposter=")([^"]*?)(")/g,
|
||||
(_match: string, pre: string, url: string, post: string) => {
|
||||
|
||||
Reference in New Issue
Block a user