@officexapp/vidfarm-devcli 0.21.29 → 0.21.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/vidfarm/SKILL.md +2 -0
- package/.agents/skills/vidfarm/recipes/cutout-graphics-for-explainers.md +18 -4
- package/.agents/skills/vidfarm/references/assets-and-sourcing.md +17 -0
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +6 -3
- package/.agents/skills/vidfarm/references/editor-workflows.md +1 -1
- package/.agents/skills/vidfarm/references/primitives.md +80 -1
- package/SKILL.director.md +124 -9
- package/SKILL.md +2 -1
- package/dist/src/cli.js +192 -11
- package/dist/src/devcli/doctor.js +13 -0
- package/dist/src/devcli/handoff.js +6 -2
- package/dist/src/devcli/hyperframes-cli.js +12 -0
- package/dist/src/devcli/sticker-pack.js +196 -2
- package/dist/src/devcli/studio-brand.js +196 -0
- package/package.json +5 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Whitelabel the LOCAL hyperframes Studio UI as VidFarm.
|
|
2
|
+
//
|
|
3
|
+
// The web editor (`/editor`, `vidfarm serve`) mounts the sealed <StudioApp/>
|
|
4
|
+
// through demo/src/VidfarmStudioEditor.tsx, which already swaps the upstream
|
|
5
|
+
// "HeyGen · HyperFrames" header logo and rewrites the product name in copy.
|
|
6
|
+
// But `hyperframes preview` (and `vidfarm hf preview`) boots the studio SPA
|
|
7
|
+
// straight out of `node_modules/hyperframes/dist/studio/index.html` — that
|
|
8
|
+
// bundle never passes through our editor entry, so it renders upstream branding
|
|
9
|
+
// on a Vidfarm director's screen.
|
|
10
|
+
//
|
|
11
|
+
// The studio server reads that index.html **per request** (studioServer.ts's
|
|
12
|
+
// SPA fallback), so a one-time on-disk patch of the shell is enough: no fork, no
|
|
13
|
+
// dist rewriting of the JS, no proxy in the middle. We inject a marker-delimited
|
|
14
|
+
// <style> + <script> block that does exactly what the web editor does in JS —
|
|
15
|
+
// hide `svg[aria-label="Hyperframes"]`, insert a VidFarm wordmark, and rename
|
|
16
|
+
// the product in text nodes / tooltips / clipboard writes.
|
|
17
|
+
//
|
|
18
|
+
// The patch is idempotent and versioned: a stale block is replaced, an up-to-date
|
|
19
|
+
// one is left alone, and a fresh `npm install` (which restores the pristine file)
|
|
20
|
+
// is re-patched on the next hyperframes spawn. `.hyperframes/` paths, the
|
|
21
|
+
// `data-hyperframes-*` DOM hooks, and CLI verbs are deliberately preserved /
|
|
22
|
+
// mapped, same as the web editor — renaming those would send an agent hunting
|
|
23
|
+
// for files that do not exist.
|
|
24
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { resolveHyperframesCli } from "./hyperframes-cli.js";
|
|
27
|
+
/** Bump when the injected block changes — older blocks are replaced in place. */
|
|
28
|
+
const BRAND_VERSION = 1;
|
|
29
|
+
const MARK_OPEN = `<!-- vidfarm-studio-brand v${BRAND_VERSION} -->`;
|
|
30
|
+
const MARK_OPEN_ANY = /<!-- vidfarm-studio-brand v\d+ -->/;
|
|
31
|
+
const MARK_CLOSE = "<!-- /vidfarm-studio-brand -->";
|
|
32
|
+
// Tab icon: a self-contained gold "V" plate (data URI, so we never have to ship
|
|
33
|
+
// or overwrite a file inside the hyperframes package). Matches the header
|
|
34
|
+
// wordmark's badge below.
|
|
35
|
+
const VIDFARM_FAVICON = "data:image/svg+xml," +
|
|
36
|
+
encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
|
|
37
|
+
'<rect width="100" height="100" rx="26" fill="#f5c518"/>' +
|
|
38
|
+
'<path d="M26 28h14l10 30 10-30h14L58 76H42z" fill="#1c1608"/>' +
|
|
39
|
+
"</svg>");
|
|
40
|
+
/**
|
|
41
|
+
* Locate `hyperframes/dist/studio/index.html` next to the resolved CLI entry.
|
|
42
|
+
* Returns null when hyperframes is not installed locally (the `npx -y` fallback
|
|
43
|
+
* path — nothing on disk of ours to patch).
|
|
44
|
+
*/
|
|
45
|
+
export function resolveStudioIndexPath() {
|
|
46
|
+
const cli = resolveHyperframesCli();
|
|
47
|
+
if (!cli)
|
|
48
|
+
return null;
|
|
49
|
+
const indexPath = path.join(path.dirname(cli), "studio", "index.html");
|
|
50
|
+
return existsSync(indexPath) ? indexPath : null;
|
|
51
|
+
}
|
|
52
|
+
/** The injected head block: hide the upstream mark, paint ours, rename the product. */
|
|
53
|
+
function brandBlock() {
|
|
54
|
+
return `${MARK_OPEN}
|
|
55
|
+
<style>
|
|
56
|
+
/* Belt-and-suspenders: kill the upstream mark before the script runs so it
|
|
57
|
+
never flashes. The wordmark below is inserted in its place. */
|
|
58
|
+
svg[aria-label="Hyperframes"] { display: none !important; }
|
|
59
|
+
</style>
|
|
60
|
+
<script>
|
|
61
|
+
(function () {
|
|
62
|
+
var MARK = "data-vidfarm-logo";
|
|
63
|
+
|
|
64
|
+
function wordmark() {
|
|
65
|
+
var el = document.createElement("div");
|
|
66
|
+
el.setAttribute(MARK, "");
|
|
67
|
+
el.style.cssText = "display:inline-flex;align-items:center;gap:8px;user-select:none;line-height:1";
|
|
68
|
+
var badge = document.createElement("span");
|
|
69
|
+
badge.style.cssText =
|
|
70
|
+
"display:inline-grid;place-items:center;width:24px;height:24px;border-radius:7px;" +
|
|
71
|
+
"background:#f5c518;color:#1c1608;font-weight:800;font-size:14px;font-family:system-ui,sans-serif";
|
|
72
|
+
badge.textContent = "V";
|
|
73
|
+
var word = document.createElement("span");
|
|
74
|
+
word.style.cssText =
|
|
75
|
+
"font-weight:800;font-size:15px;letter-spacing:-.01em;color:#fffbe6;font-family:system-ui,sans-serif";
|
|
76
|
+
word.textContent = "VidFarm";
|
|
77
|
+
el.appendChild(badge);
|
|
78
|
+
el.appendChild(word);
|
|
79
|
+
return el;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function swapLogo() {
|
|
83
|
+
var logos = document.querySelectorAll('svg[aria-label="Hyperframes"]');
|
|
84
|
+
for (var i = 0; i < logos.length; i += 1) {
|
|
85
|
+
var svg = logos[i];
|
|
86
|
+
var prev = svg.previousElementSibling;
|
|
87
|
+
if ((prev && prev.hasAttribute && prev.hasAttribute(MARK)) || svg.getAttribute("data-vidfarm-hidden") === "1") continue;
|
|
88
|
+
svg.setAttribute("data-vidfarm-hidden", "1");
|
|
89
|
+
svg.style.display = "none";
|
|
90
|
+
if (svg.parentElement) svg.parentElement.insertBefore(wordmark(), svg);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Same rewrite table as the web editor: CLI verbs and .hyperframes/ paths are
|
|
95
|
+
// matched BEFORE the bare product name, so real files/commands survive.
|
|
96
|
+
function rewrite(text) {
|
|
97
|
+
return text
|
|
98
|
+
.replace(/\\bhyperframes (render|preview|lint|check|publish|init|add)\\b/g, "vidfarm $1")
|
|
99
|
+
.replace(/(^|[^./\\w-])[Hh]yper[Ff]rames(?![./\\w-])/g, "$1Vidfarm")
|
|
100
|
+
.replace(/(^|[^./\\w-])[Hh]yper[Ff]rame(?![s./\\w-])/g, "$1Vidfarm")
|
|
101
|
+
.replace(/\\bHeyGen\\b/g, "Vidfarm");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function needsRewrite(text) {
|
|
105
|
+
return /[Hh]yper[Ff]rame|HeyGen/.test(text) && rewrite(text) !== text;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function swapText() {
|
|
109
|
+
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
110
|
+
var pending = [];
|
|
111
|
+
for (var node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
112
|
+
var tag = node.parentElement && node.parentElement.tagName;
|
|
113
|
+
if (tag === "SCRIPT" || tag === "STYLE") continue;
|
|
114
|
+
if (needsRewrite(node.nodeValue || "")) pending.push(node);
|
|
115
|
+
}
|
|
116
|
+
for (var i = 0; i < pending.length; i += 1) pending[i].nodeValue = rewrite(pending[i].nodeValue || "");
|
|
117
|
+
var attrs = ["title", "aria-label", "placeholder"];
|
|
118
|
+
var els = document.querySelectorAll("[title],[aria-label],[placeholder]");
|
|
119
|
+
for (var j = 0; j < els.length; j += 1) {
|
|
120
|
+
for (var k = 0; k < attrs.length; k += 1) {
|
|
121
|
+
var value = els[j].getAttribute(attrs[k]);
|
|
122
|
+
if (value && needsRewrite(value)) els[j].setAttribute(attrs[k], rewrite(value));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function apply() {
|
|
128
|
+
swapLogo();
|
|
129
|
+
swapText();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function start() {
|
|
133
|
+
apply();
|
|
134
|
+
new MutationObserver(apply).observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
135
|
+
// Lint / storyboard panels hand agent prompts to the clipboard — rebrand on the way out.
|
|
136
|
+
try {
|
|
137
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
138
|
+
var original = navigator.clipboard.writeText.bind(navigator.clipboard);
|
|
139
|
+
navigator.clipboard.writeText = function (data) {
|
|
140
|
+
return original(typeof data === "string" ? rewrite(data) : data);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
} catch (err) { /* clipboard locked down — branding is best-effort */ }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (document.body) start();
|
|
147
|
+
else document.addEventListener("DOMContentLoaded", start);
|
|
148
|
+
})();
|
|
149
|
+
</script>
|
|
150
|
+
${MARK_CLOSE}`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Patch the installed studio shell in place. Never throws — a read-only or
|
|
154
|
+
* missing node_modules just means the director sees upstream branding, which is
|
|
155
|
+
* cosmetic and must not fail a render.
|
|
156
|
+
*/
|
|
157
|
+
export function applyVidfarmStudioBrand(indexPathOverride) {
|
|
158
|
+
const indexPath = indexPathOverride ?? resolveStudioIndexPath();
|
|
159
|
+
if (!indexPath)
|
|
160
|
+
return "unavailable";
|
|
161
|
+
let html;
|
|
162
|
+
try {
|
|
163
|
+
html = readFileSync(indexPath, "utf-8");
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return "unavailable";
|
|
167
|
+
}
|
|
168
|
+
const current = brandBlock();
|
|
169
|
+
if (html.includes(MARK_OPEN) && html.includes(MARK_CLOSE)) {
|
|
170
|
+
// Up-to-date block already present (same version) — but the block body can
|
|
171
|
+
// still drift if BRAND_VERSION was not bumped during development, so compare.
|
|
172
|
+
if (html.includes(current))
|
|
173
|
+
return "already-branded";
|
|
174
|
+
}
|
|
175
|
+
// Strip any previous (older-version or drifted) block before re-injecting.
|
|
176
|
+
let out = html.replace(new RegExp(`${MARK_OPEN_ANY.source}[\\s\\S]*?${MARK_CLOSE.replace(/[/]/g, "\\/")}`), "");
|
|
177
|
+
out = out.replace(/<title>[\s\S]*?<\/title>/, "<title>VidFarm Studio</title>");
|
|
178
|
+
// Tab icon (no-op once already swapped — the upstream href is gone by then).
|
|
179
|
+
out = out.replace(/href="\/favicon\.svg"/, `href="${VIDFARM_FAVICON}"`);
|
|
180
|
+
if (out.includes("</head>")) {
|
|
181
|
+
out = out.replace("</head>", `${current}\n </head>`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
out = `${current}\n${out}`;
|
|
185
|
+
}
|
|
186
|
+
if (out === html)
|
|
187
|
+
return "already-branded";
|
|
188
|
+
try {
|
|
189
|
+
writeFileSync(indexPath, out, "utf-8");
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return "unavailable";
|
|
193
|
+
}
|
|
194
|
+
return "patched";
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=studio-brand.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@officexapp/vidfarm-devcli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.31",
|
|
4
4
|
"description": "Local bridge for the Vidfarm Trackpad Editor. `vidfarm serve <template_id>` boots the FULL editor on localhost (disk-backed records/storage, free in-process render); edit composition.html on disk (Claude Code, Codex, etc.) and the browser live-morphs it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dist/src/devcli/skills.js",
|
|
33
33
|
"dist/src/devcli/speech.js",
|
|
34
34
|
"dist/src/devcli/sticker-pack.js",
|
|
35
|
+
"dist/src/devcli/studio-brand.js",
|
|
35
36
|
"dist/src/devcli/stills.js",
|
|
36
37
|
"dist/src/devcli/storyboard.js",
|
|
37
38
|
"dist/src/devcli/telemetry.js",
|
|
@@ -93,6 +94,9 @@
|
|
|
93
94
|
"check": "tsc -p tsconfig.json --noEmit && npm run check:skills",
|
|
94
95
|
"test:clips": "node --import tsx --test test/clip-curation.test.ts",
|
|
95
96
|
"test:qa": "node --import tsx --test test/qa-check.test.ts",
|
|
97
|
+
"test:studio-brand": "node --import tsx --test test/studio-brand.test.ts",
|
|
98
|
+
"test:stickers": "node --import tsx --test test/sticker-pack.test.ts",
|
|
99
|
+
"test:social-recycle": "node --import tsx --test test/social-recycle.test.ts",
|
|
96
100
|
"check:skills": "node scripts/build-director-skill-rollup.mjs --check && node scripts/check-skill-routes.mjs",
|
|
97
101
|
"benchmark:editor-chat": "node --import tsx scripts/benchmark-editor-chat-harness.mjs",
|
|
98
102
|
"cdk:deploy:prod-serverless": "npm run build && dotenv -e .env.production -- npx aws-cdk deploy --app 'node dist/infra/cdk/bin/vidfarm-prod.js'",
|