hyperframes 0.8.57 → 0.8.59

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.
Files changed (182) hide show
  1. package/dist/cli.js +5300 -3428
  2. package/dist/docs/data-attributes.md +2 -1
  3. package/dist/fontLocalizeCli.js +73 -12
  4. package/dist/hyperframe-runtime.js +25 -25
  5. package/dist/hyperframe.manifest.json +1 -1
  6. package/dist/hyperframe.runtime.iife.js +25 -25
  7. package/dist/hyperframes-player.global.js +3 -3
  8. package/dist/registry/localSemantic.js +548 -0
  9. package/dist/skills/hyperframes-cli/SKILL.md +2 -2
  10. package/dist/skills/media-use/SKILL.md +96 -0
  11. package/dist/skills/media-use/audio/assets/sfx/CREDITS.md +35 -0
  12. package/dist/skills/media-use/audio/assets/sfx/chime.mp3 +0 -0
  13. package/dist/skills/media-use/audio/assets/sfx/click-soft.mp3 +0 -0
  14. package/dist/skills/media-use/audio/assets/sfx/click.mp3 +0 -0
  15. package/dist/skills/media-use/audio/assets/sfx/error.mp3 +0 -0
  16. package/dist/skills/media-use/audio/assets/sfx/glitch-1.mp3 +0 -0
  17. package/dist/skills/media-use/audio/assets/sfx/glitch-2.mp3 +0 -0
  18. package/dist/skills/media-use/audio/assets/sfx/glitch-3.mp3 +0 -0
  19. package/dist/skills/media-use/audio/assets/sfx/impact-bass-1.mp3 +0 -0
  20. package/dist/skills/media-use/audio/assets/sfx/impact-bass-2.mp3 +0 -0
  21. package/dist/skills/media-use/audio/assets/sfx/key-press.mp3 +0 -0
  22. package/dist/skills/media-use/audio/assets/sfx/manifest.json +97 -0
  23. package/dist/skills/media-use/audio/assets/sfx/notification.mp3 +0 -0
  24. package/dist/skills/media-use/audio/assets/sfx/ping.mp3 +0 -0
  25. package/dist/skills/media-use/audio/assets/sfx/pop.mp3 +0 -0
  26. package/dist/skills/media-use/audio/assets/sfx/riser.mp3 +0 -0
  27. package/dist/skills/media-use/audio/assets/sfx/sparkle.mp3 +0 -0
  28. package/dist/skills/media-use/audio/assets/sfx/typing.mp3 +0 -0
  29. package/dist/skills/media-use/audio/assets/sfx/whoosh-cinematic.mp3 +0 -0
  30. package/dist/skills/media-use/audio/assets/sfx/whoosh-short.mp3 +0 -0
  31. package/dist/skills/media-use/audio/assets/sfx/whoosh.mp3 +0 -0
  32. package/dist/skills/media-use/audio/references/bgm.md +74 -0
  33. package/dist/skills/media-use/audio/references/captions/authoring.md +163 -0
  34. package/dist/skills/media-use/audio/references/captions/motion.md +87 -0
  35. package/dist/skills/media-use/audio/references/captions/transcript-handling.md +97 -0
  36. package/dist/skills/media-use/audio/references/remove-background.md +143 -0
  37. package/dist/skills/media-use/audio/references/requirements.md +29 -0
  38. package/dist/skills/media-use/audio/references/sfx.md +42 -0
  39. package/dist/skills/media-use/audio/references/transcribe.md +52 -0
  40. package/dist/skills/media-use/audio/references/tts-to-captions.md +26 -0
  41. package/dist/skills/media-use/audio/references/tts.md +165 -0
  42. package/dist/skills/media-use/audio/scripts/audio.mjs +295 -0
  43. package/dist/skills/media-use/audio/scripts/audio.test.mjs +49 -0
  44. package/dist/skills/media-use/audio/scripts/heygen-tts.mjs +121 -0
  45. package/dist/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
  46. package/dist/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
  47. package/dist/skills/media-use/audio/scripts/lib/bgm.mjs +257 -0
  48. package/dist/skills/media-use/audio/scripts/lib/bgm.test.mjs +30 -0
  49. package/dist/skills/media-use/audio/scripts/lib/concurrency.mjs +14 -0
  50. package/dist/skills/media-use/audio/scripts/lib/concurrency.test.mjs +41 -0
  51. package/dist/skills/media-use/audio/scripts/lib/heygen.mjs +156 -0
  52. package/dist/skills/media-use/audio/scripts/lib/heygen.test.mjs +103 -0
  53. package/dist/skills/media-use/audio/scripts/lib/python.mjs +63 -0
  54. package/dist/skills/media-use/audio/scripts/lib/python.test.mjs +68 -0
  55. package/dist/skills/media-use/audio/scripts/lib/sfx.mjs +143 -0
  56. package/dist/skills/media-use/audio/scripts/lib/sfx.test.mjs +69 -0
  57. package/dist/skills/media-use/audio/scripts/lib/tts.mjs +379 -0
  58. package/dist/skills/media-use/audio/scripts/lib/tts.spawn.test.mjs +158 -0
  59. package/dist/skills/media-use/audio/scripts/lib/tts.test.mjs +157 -0
  60. package/dist/skills/media-use/audio/scripts/lyria-recipe.py +136 -0
  61. package/dist/skills/media-use/audio/scripts/wait-bgm.mjs +171 -0
  62. package/dist/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
  63. package/dist/skills/media-use/luts/README.md +28 -0
  64. package/dist/skills/media-use/luts/index.json +57 -0
  65. package/dist/skills/media-use/references/audio.md +19 -0
  66. package/dist/skills/media-use/references/grading.md +160 -0
  67. package/dist/skills/media-use/references/media-treatment-recipes.md +929 -0
  68. package/dist/skills/media-use/references/media-treatments.md +202 -0
  69. package/dist/skills/media-use/references/memory.md +36 -0
  70. package/dist/skills/media-use/references/meta.md +61 -0
  71. package/dist/skills/media-use/references/operations.md +325 -0
  72. package/dist/skills/media-use/references/resolve.md +150 -0
  73. package/dist/skills/media-use/references/setup-providers.md +80 -0
  74. package/dist/skills/media-use/references/telemetry-dashboard.md +49 -0
  75. package/dist/skills/media-use/registry/localSemantic.js +548 -0
  76. package/dist/skills/media-use/scripts/audio-duck.mjs +113 -0
  77. package/dist/skills/media-use/scripts/compatibility.test.mjs +27 -0
  78. package/dist/skills/media-use/scripts/dither.mjs +328 -0
  79. package/dist/skills/media-use/scripts/dither.test.mjs +176 -0
  80. package/dist/skills/media-use/scripts/eval.mjs +369 -0
  81. package/dist/skills/media-use/scripts/lib/adopt.mjs +128 -0
  82. package/dist/skills/media-use/scripts/lib/adopt.test.mjs +95 -0
  83. package/dist/skills/media-use/scripts/lib/bgm-provider.mjs +20 -0
  84. package/dist/skills/media-use/scripts/lib/brand-provider.mjs +59 -0
  85. package/dist/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +119 -0
  86. package/dist/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +139 -0
  87. package/dist/skills/media-use/scripts/lib/cache.mjs +150 -0
  88. package/dist/skills/media-use/scripts/lib/candidates.mjs +91 -0
  89. package/dist/skills/media-use/scripts/lib/candidates.test.mjs +155 -0
  90. package/dist/skills/media-use/scripts/lib/codex-provider.mjs +161 -0
  91. package/dist/skills/media-use/scripts/lib/codex-provider.test.mjs +34 -0
  92. package/dist/skills/media-use/scripts/lib/coverage.test.mjs +132 -0
  93. package/dist/skills/media-use/scripts/lib/cube-build.mjs +181 -0
  94. package/dist/skills/media-use/scripts/lib/cube-build.test.mjs +80 -0
  95. package/dist/skills/media-use/scripts/lib/cube-validate.mjs +198 -0
  96. package/dist/skills/media-use/scripts/lib/cube-validate.test.mjs +125 -0
  97. package/dist/skills/media-use/scripts/lib/cutlist.mjs +184 -0
  98. package/dist/skills/media-use/scripts/lib/cutlist.test.mjs +162 -0
  99. package/dist/skills/media-use/scripts/lib/duck.mjs +95 -0
  100. package/dist/skills/media-use/scripts/lib/duck.test.mjs +153 -0
  101. package/dist/skills/media-use/scripts/lib/error-diffusion.mjs +230 -0
  102. package/dist/skills/media-use/scripts/lib/error-diffusion.test.mjs +102 -0
  103. package/dist/skills/media-use/scripts/lib/freeze.mjs +62 -0
  104. package/dist/skills/media-use/scripts/lib/freeze.test.mjs +46 -0
  105. package/dist/skills/media-use/scripts/lib/grade-analyzer.mjs +265 -0
  106. package/dist/skills/media-use/scripts/lib/grade-analyzer.test.mjs +285 -0
  107. package/dist/skills/media-use/scripts/lib/heygen-cli.mjs +191 -0
  108. package/dist/skills/media-use/scripts/lib/heygen-cli.test.mjs +301 -0
  109. package/dist/skills/media-use/scripts/lib/heygen-search.mjs +45 -0
  110. package/dist/skills/media-use/scripts/lib/heygen-search.test.mjs +49 -0
  111. package/dist/skills/media-use/scripts/lib/heygen-video-provider.mjs +138 -0
  112. package/dist/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +435 -0
  113. package/dist/skills/media-use/scripts/lib/image-provider.mjs +44 -0
  114. package/dist/skills/media-use/scripts/lib/index-gen.mjs +63 -0
  115. package/dist/skills/media-use/scripts/lib/local-media-search.mjs +32 -0
  116. package/dist/skills/media-use/scripts/lib/local-models.mjs +318 -0
  117. package/dist/skills/media-use/scripts/lib/local-models.test.mjs +220 -0
  118. package/dist/skills/media-use/scripts/lib/local-run.mjs +78 -0
  119. package/dist/skills/media-use/scripts/lib/local-run.test.mjs +121 -0
  120. package/dist/skills/media-use/scripts/lib/logo-provider.mjs +223 -0
  121. package/dist/skills/media-use/scripts/lib/logo-provider.test.mjs +154 -0
  122. package/dist/skills/media-use/scripts/lib/ltx-video-provider.mjs +94 -0
  123. package/dist/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +316 -0
  124. package/dist/skills/media-use/scripts/lib/lut-preset-provider.mjs +271 -0
  125. package/dist/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +199 -0
  126. package/dist/skills/media-use/scripts/lib/manifest.mjs +229 -0
  127. package/dist/skills/media-use/scripts/lib/manifest.test.mjs +388 -0
  128. package/dist/skills/media-use/scripts/lib/match.mjs +47 -0
  129. package/dist/skills/media-use/scripts/lib/media-fetch.mjs +71 -0
  130. package/dist/skills/media-use/scripts/lib/media-fetch.test.mjs +169 -0
  131. package/dist/skills/media-use/scripts/lib/media-search.d.mts +9 -0
  132. package/dist/skills/media-use/scripts/lib/media-search.mjs +107 -0
  133. package/dist/skills/media-use/scripts/lib/mflux-provider.mjs +128 -0
  134. package/dist/skills/media-use/scripts/lib/mflux-provider.test.mjs +174 -0
  135. package/dist/skills/media-use/scripts/lib/misses.mjs +49 -0
  136. package/dist/skills/media-use/scripts/lib/misses.test.mjs +78 -0
  137. package/dist/skills/media-use/scripts/lib/npx-sync.mjs +31 -0
  138. package/dist/skills/media-use/scripts/lib/npx-sync.test.mjs +50 -0
  139. package/dist/skills/media-use/scripts/lib/parakeet-words.mjs +27 -0
  140. package/dist/skills/media-use/scripts/lib/parakeet-words.test.mjs +44 -0
  141. package/dist/skills/media-use/scripts/lib/prefs-store.mjs +180 -0
  142. package/dist/skills/media-use/scripts/lib/prefs-store.test.mjs +189 -0
  143. package/dist/skills/media-use/scripts/lib/probe.mjs +39 -0
  144. package/dist/skills/media-use/scripts/lib/probe.test.mjs +31 -0
  145. package/dist/skills/media-use/scripts/lib/providers.mjs +5 -0
  146. package/dist/skills/media-use/scripts/lib/recipe-store.mjs +367 -0
  147. package/dist/skills/media-use/scripts/lib/recipe-store.test.mjs +367 -0
  148. package/dist/skills/media-use/scripts/lib/registry.mjs +218 -0
  149. package/dist/skills/media-use/scripts/lib/registry.test.mjs +262 -0
  150. package/dist/skills/media-use/scripts/lib/search.mjs +24 -0
  151. package/dist/skills/media-use/scripts/lib/search.test.mjs +32 -0
  152. package/dist/skills/media-use/scripts/lib/sfx-provider.mjs +23 -0
  153. package/dist/skills/media-use/scripts/lib/specs.mjs +132 -0
  154. package/dist/skills/media-use/scripts/lib/specs.test.mjs +157 -0
  155. package/dist/skills/media-use/scripts/lib/stats.mjs +117 -0
  156. package/dist/skills/media-use/scripts/lib/stats.test.mjs +165 -0
  157. package/dist/skills/media-use/scripts/lib/telemetry.mjs +193 -0
  158. package/dist/skills/media-use/scripts/lib/telemetry.test.mjs +373 -0
  159. package/dist/skills/media-use/scripts/lib/tts-local-provider.mjs +92 -0
  160. package/dist/skills/media-use/scripts/lib/tts-local-provider.test.mjs +72 -0
  161. package/dist/skills/media-use/scripts/lib/usage.mjs +57 -0
  162. package/dist/skills/media-use/scripts/lib/usage.test.mjs +54 -0
  163. package/dist/skills/media-use/scripts/lib/voice-provider.mjs +57 -0
  164. package/dist/skills/media-use/scripts/lib/voice-provider.test.mjs +46 -0
  165. package/dist/skills/media-use/scripts/lib/words.mjs +18 -0
  166. package/dist/skills/media-use/scripts/prefs.mjs +70 -0
  167. package/dist/skills/media-use/scripts/recipe.mjs +103 -0
  168. package/dist/skills/media-use/scripts/resolve.mjs +1287 -0
  169. package/dist/skills/media-use/scripts/transcribe.mjs +180 -0
  170. package/dist/skills/media-use/scripts/transcript-cut.mjs +239 -0
  171. package/dist/skills/registry/localSemantic.js +548 -0
  172. package/dist/studio/assets/{hyperframes-player-c23wnIfc.js → hyperframes-player-BBub3ZdI.js} +3 -3
  173. package/dist/studio/assets/{index-IDNr3X1H.js → index-BxZ5Cw5w.js} +1 -1
  174. package/dist/studio/assets/{index-DgkAzx3n.js → index-CI7WON-E.js} +1 -1
  175. package/dist/studio/assets/{index-gbOCDCuZ.js → index-D1KNPijK.js} +192 -192
  176. package/dist/studio/assets/{index-DKJpHYYd.css → index-DZBHG9TW.css} +1 -1
  177. package/dist/studio/index.d.ts +577 -8
  178. package/dist/studio/index.html +2 -2
  179. package/dist/studio/index.js +22537 -21600
  180. package/dist/studio/index.js.map +1 -1
  181. package/dist/studio/styles.css +1 -1
  182. package/package.json +3 -3
@@ -0,0 +1,548 @@
1
+ import { createRequire as __hf_createRequire } from "node:module";
2
+ import { fileURLToPath as __hf_fileURLToPath } from "node:url";
3
+ import { dirname as __hf_dirname } from "node:path";
4
+ var require = __hf_createRequire(import.meta.url);
5
+ var __filename = __hf_fileURLToPath(import.meta.url);
6
+ var __dirname = __hf_dirname(__filename);
7
+
8
+ // src/registry/localSemantic.ts
9
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
10
+ import { homedir as homedir4 } from "os";
11
+ import { join as join4 } from "path";
12
+
13
+ // src/registry/localEmbedder.ts
14
+ import { readFileSync as readFileSync4 } from "fs";
15
+
16
+ // src/utils/optionalPackages.ts
17
+ import { spawn } from "child_process";
18
+ import { randomUUID } from "crypto";
19
+ import {
20
+ existsSync,
21
+ mkdirSync,
22
+ readdirSync,
23
+ readFileSync,
24
+ renameSync,
25
+ rmSync,
26
+ writeFileSync
27
+ } from "fs";
28
+ import { createRequire } from "module";
29
+ import { homedir } from "os";
30
+ import { basename, dirname, join } from "path";
31
+
32
+ // src/utils/npxCommand.ts
33
+ function buildCmdShimCommand(name, args, platform) {
34
+ if (platform === "win32") {
35
+ return { command: "cmd.exe", args: ["/d", "/s", "/c", `${name}.cmd`, ...args] };
36
+ }
37
+ return { command: name, args: [...args] };
38
+ }
39
+ function buildNpmCommand(args, platform = process.platform) {
40
+ return buildCmdShimCommand("npm", args, platform);
41
+ }
42
+
43
+ // src/utils/optionalPackages.ts
44
+ var OPTIONAL_PACKAGES = {
45
+ "onnxruntime-node": "1.21.1",
46
+ "@google/genai": "1.52.0"
47
+ };
48
+ var CACHE_DIR = join(homedir(), ".cache", "hyperframes", "optional");
49
+ function optionalPackageDir(name, cacheDir = CACHE_DIR) {
50
+ return join(cacheDir, `${name.replace("/", "__")}@${OPTIONAL_PACKAGES[name]}`);
51
+ }
52
+ async function loadOptionalPackage(name, feature, deps = defaultDeps) {
53
+ const dir = optionalPackageDir(name, deps.cacheDir);
54
+ const installed = deps.loadInstalled(dir, name);
55
+ if (installed !== null) return installed;
56
+ const version = OPTIONAL_PACKAGES[name];
57
+ deps.log(`installing ${name} for ${feature}, once`);
58
+ try {
59
+ await deps.install(dir, name, version);
60
+ } catch (err) {
61
+ const output = err.message.trim();
62
+ const advice = NETWORK_FAILURE.test(output) ? "Check your network connection, then retry" : "Retry";
63
+ throw new Error(
64
+ `${feature} needs ${name}, and installing it failed (${output.split("\n")[0]}). ${advice}, or install it yourself: npm install ${name}@${version} --prefix "${dir}"`
65
+ );
66
+ }
67
+ const loaded = deps.loadInstalled(dir, name);
68
+ if (loaded === null) {
69
+ throw new Error(`${name}@${version} installed into ${dir} but could not be loaded.`);
70
+ }
71
+ return loaded;
72
+ }
73
+ var NETWORK_FAILURE = /ENOTFOUND|ETIMEDOUT|EAI_AGAIN|ECONNRESET|ECONNREFUSED|network/i;
74
+ function manifestPath(dir, name) {
75
+ return join(dir, "node_modules", name, "package.json");
76
+ }
77
+ function isInstalled(dir, name) {
78
+ return existsSync(manifestPath(dir, name));
79
+ }
80
+ function loadInstalled(dir, name) {
81
+ if (!isInstalled(dir, name)) return null;
82
+ return createRequire(join(dir, "package.json"))(name);
83
+ }
84
+ function runNpm(args) {
85
+ const npm = buildNpmCommand(args);
86
+ const child = spawn(npm.command, npm.args, { stdio: ["ignore", "pipe", "pipe"] });
87
+ return new Promise((resolve, reject) => {
88
+ let output = "";
89
+ child.stdout.on("data", (chunk) => output += chunk);
90
+ child.stderr.on("data", (chunk) => output += chunk);
91
+ child.on("error", reject);
92
+ child.on(
93
+ "close",
94
+ (code) => code === 0 ? resolve() : reject(new Error(output || `npm exited with code ${code}`))
95
+ );
96
+ });
97
+ }
98
+ function isProcessAlive(pid) {
99
+ try {
100
+ process.kill(pid, 0);
101
+ return true;
102
+ } catch (err) {
103
+ return err.code !== "ESRCH";
104
+ }
105
+ }
106
+ function sweepStaleStaging(dir) {
107
+ const prefix = `${basename(dir)}.tmp-`;
108
+ const parent = dirname(dir);
109
+ if (!existsSync(parent)) return;
110
+ for (const entry of readdirSync(parent)) {
111
+ if (!entry.startsWith(prefix)) continue;
112
+ const pid = /^(\d+)(?:-|$)/.exec(entry.slice(prefix.length))?.[1];
113
+ if (pid === void 0 || isProcessAlive(Number(pid))) continue;
114
+ const stale = join(parent, entry);
115
+ try {
116
+ rmSync(stale, { recursive: true, force: true });
117
+ } catch (err) {
118
+ console.error(`could not remove stale install dir ${stale}: ${err.message}`);
119
+ }
120
+ }
121
+ }
122
+ async function install(dir, name, version, run = runNpm) {
123
+ sweepStaleStaging(dir);
124
+ const staging = `${dir}.tmp-${process.pid}-${randomUUID().slice(0, 8)}`;
125
+ mkdirSync(staging, { recursive: true });
126
+ writeFileSync(join(staging, "package.json"), "{}");
127
+ try {
128
+ await run([
129
+ "install",
130
+ `${name}@${version}`,
131
+ "--prefix",
132
+ staging,
133
+ "--no-audit",
134
+ "--no-fund",
135
+ "--loglevel=error",
136
+ // A first-use install is interactive, not a CI resolve: fail fast and name the
137
+ // manual command instead of sitting through npm's default multi-minute backoff.
138
+ "--fetch-retries=0",
139
+ "--fetch-timeout=20000"
140
+ ]);
141
+ if (isInstalled(dir, name)) return;
142
+ rmSync(dir, { recursive: true, force: true });
143
+ renameSync(staging, dir);
144
+ } catch (err) {
145
+ if (!isInstalled(dir, name)) throw err;
146
+ } finally {
147
+ rmSync(staging, { recursive: true, force: true });
148
+ }
149
+ }
150
+ var defaultDeps = {
151
+ cacheDir: CACHE_DIR,
152
+ loadInstalled,
153
+ install,
154
+ log: (line) => console.error(line)
155
+ };
156
+
157
+ // src/registry/localModel.ts
158
+ import { createHash } from "crypto";
159
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, unlinkSync as unlinkSync2 } from "fs";
160
+ import { homedir as homedir3 } from "os";
161
+ import { join as join3 } from "path";
162
+
163
+ // src/utils/download.ts
164
+ import { randomUUID as randomUUID2 } from "crypto";
165
+ import { createWriteStream, renameSync as renameSync2, unlinkSync } from "fs";
166
+ import { get as httpsGet } from "https";
167
+ import { Transform } from "stream";
168
+ import { pipeline } from "stream/promises";
169
+
170
+ // src/telemetry/config.ts
171
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
172
+ import { join as join2 } from "path";
173
+ import { homedir as homedir2 } from "os";
174
+ import { randomUUID as randomUUID3 } from "crypto";
175
+ var CONFIG_DIR = join2(homedir2(), ".hyperframes");
176
+ var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
177
+ var STATE_FILE = join2(CONFIG_DIR, "install-state.json");
178
+ var LEGACY_STATE_FILE = join2(homedir2(), ".local", "state", "hyperframes", "install-state.json");
179
+
180
+ // src/registry/localModel.ts
181
+ var LOCAL_MODEL_ID = "bge-small-en-v1.5";
182
+ var LOCAL_MODEL_DIMENSIONS = 384;
183
+ var QUERY_INSTRUCTION = "Represent this sentence for searching relevant passages: ";
184
+ var MODELS_DIR = join3(homedir3(), ".hyperframes", "models");
185
+ var MODEL_REPO = "Xenova/bge-small-en-v1.5";
186
+ var LOCAL_MODEL_REVISION = "ea104dacec62c0de699686887e3f920caeb4f3e3";
187
+ var LOCAL_MODEL_ARTIFACTS = [
188
+ {
189
+ url: `https://huggingface.co/${MODEL_REPO}/resolve/${LOCAL_MODEL_REVISION}/onnx/model_quantized.onnx`,
190
+ dest: () => localModelPath(),
191
+ bytes: 34014426,
192
+ sha256: "6c9c6101a956d62dfb5e7190c538226c0c5bb9cb27b651234b6df063ee7dbfe4"
193
+ },
194
+ {
195
+ url: `https://huggingface.co/${MODEL_REPO}/resolve/${LOCAL_MODEL_REVISION}/tokenizer.json`,
196
+ dest: () => localTokenizerPath(),
197
+ bytes: 711396,
198
+ sha256: "d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"
199
+ }
200
+ ];
201
+ function localModelPath() {
202
+ return join3(MODELS_DIR, `${LOCAL_MODEL_ID}.onnx`);
203
+ }
204
+ function localTokenizerPath() {
205
+ return join3(MODELS_DIR, `${LOCAL_MODEL_ID}.tokenizer.json`);
206
+ }
207
+ function modelFileIsValid(file) {
208
+ const path = file.dest();
209
+ if (!existsSync3(path)) return false;
210
+ try {
211
+ return createHash("sha256").update(readFileSync3(path)).digest("hex") === file.sha256;
212
+ } catch {
213
+ return false;
214
+ }
215
+ }
216
+ function isLocalModelReady() {
217
+ return LOCAL_MODEL_ARTIFACTS.every(modelFileIsValid);
218
+ }
219
+
220
+ // src/registry/wordpiece.ts
221
+ function configFromTokenizerJson(raw) {
222
+ const parsed = JSON.parse(raw);
223
+ const model = parsed.model;
224
+ if (!model?.vocab) throw new Error("tokenizer.json has no WordPiece vocab");
225
+ return {
226
+ vocab: model.vocab,
227
+ unkToken: model.unk_token ?? "[UNK]",
228
+ continuingSubwordPrefix: model.continuing_subword_prefix ?? "##",
229
+ maxInputCharsPerWord: model.max_input_chars_per_word ?? 100,
230
+ clsToken: "[CLS]",
231
+ sepToken: "[SEP]"
232
+ };
233
+ }
234
+ function normalize(text) {
235
+ let out = "";
236
+ for (const char of text) {
237
+ const code = char.codePointAt(0);
238
+ if (code === 0 || code === 65533) continue;
239
+ if (isControl(char, code)) continue;
240
+ if (isWhitespace(char, code)) {
241
+ out += " ";
242
+ continue;
243
+ }
244
+ out += isChinese(code) ? ` ${char} ` : char;
245
+ }
246
+ return out.toLowerCase().normalize("NFD").replace(new RegExp("\\p{Mn}", "gu"), "");
247
+ }
248
+ function preTokenize(normalized) {
249
+ const words = [];
250
+ for (const chunk of normalized.split(/\s+/)) {
251
+ if (!chunk) continue;
252
+ let current = "";
253
+ for (const char of chunk) {
254
+ if (isPunctuation(char)) {
255
+ if (current) words.push(current);
256
+ words.push(char);
257
+ current = "";
258
+ } else {
259
+ current += char;
260
+ }
261
+ }
262
+ if (current) words.push(current);
263
+ }
264
+ return words;
265
+ }
266
+ function wordPiece(word, config) {
267
+ if ([...word].length > config.maxInputCharsPerWord) return [config.unkToken];
268
+ const pieces = [];
269
+ let start = 0;
270
+ while (start < word.length) {
271
+ let end = word.length;
272
+ let match = null;
273
+ while (start < end) {
274
+ const candidate = start === 0 ? word.slice(start, end) : config.continuingSubwordPrefix + word.slice(start, end);
275
+ if (candidate in config.vocab) {
276
+ match = candidate;
277
+ break;
278
+ }
279
+ end -= 1;
280
+ }
281
+ if (match === null) return [config.unkToken];
282
+ pieces.push(match);
283
+ start = end;
284
+ }
285
+ return pieces;
286
+ }
287
+ function encode(text, config) {
288
+ const tokens = [config.clsToken];
289
+ for (const word of preTokenize(normalize(text))) tokens.push(...wordPiece(word, config));
290
+ tokens.push(config.sepToken);
291
+ const unk = config.vocab[config.unkToken];
292
+ const ids = tokens.map((token) => config.vocab[token] ?? unk);
293
+ return {
294
+ ids,
295
+ attentionMask: ids.map(() => 1),
296
+ tokenTypeIds: ids.map(() => 0)
297
+ };
298
+ }
299
+ function isControl(char, code) {
300
+ if (char === " " || char === "\n" || char === "\r") return false;
301
+ return code < 32 || code === 127 || new RegExp("\\p{Cc}|\\p{Cf}", "u").test(char);
302
+ }
303
+ function isWhitespace(char, code) {
304
+ return char === " " || char === " " || char === "\n" || char === "\r" || code === 11 || code === 12 || new RegExp("\\p{Zs}", "u").test(char);
305
+ }
306
+ function isPunctuation(char) {
307
+ const code = char.codePointAt(0);
308
+ const asciiSymbol = code >= 33 && code <= 47 || code >= 58 && code <= 64 || code >= 91 && code <= 96 || code >= 123 && code <= 126;
309
+ return asciiSymbol || new RegExp("\\p{P}", "u").test(char);
310
+ }
311
+ function isChinese(code) {
312
+ return code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983 || code >= 63744 && code <= 64255 || code >= 194560 && code <= 195103;
313
+ }
314
+
315
+ // src/registry/localEmbedder.ts
316
+ var MAX_TOKENS = 512;
317
+ var FEATURE = "on-device search";
318
+ async function loadLocalEmbedder() {
319
+ const ort = await loadOptionalPackage("onnxruntime-node", FEATURE);
320
+ const config = configFromTokenizerJson(readFileSync4(localTokenizerPath(), "utf-8"));
321
+ const session = await ort.InferenceSession.create(localModelPath());
322
+ return {
323
+ async embed(texts, options) {
324
+ if (texts.length === 0) return [];
325
+ const prefix = options?.isQuery ? QUERY_INSTRUCTION : "";
326
+ const encodings = texts.map((text) => truncate(encode(prefix + text, config)));
327
+ const width = Math.max(...encodings.map((e) => e.ids.length));
328
+ const batch = encodings.length;
329
+ const ids = new BigInt64Array(batch * width);
330
+ const mask = new BigInt64Array(batch * width);
331
+ const types = new BigInt64Array(batch * width);
332
+ encodings.forEach((encoding, row) => {
333
+ encoding.ids.forEach((id, column) => {
334
+ const at = row * width + column;
335
+ ids[at] = BigInt(id);
336
+ mask[at] = 1n;
337
+ types[at] = 0n;
338
+ });
339
+ });
340
+ const dims = [batch, width];
341
+ const output = await session.run({
342
+ input_ids: new ort.Tensor("int64", ids, dims),
343
+ attention_mask: new ort.Tensor("int64", mask, dims),
344
+ token_type_ids: new ort.Tensor("int64", types, dims)
345
+ });
346
+ const hidden = output["last_hidden_state"];
347
+ if (!hidden) throw new Error("model returned no last_hidden_state");
348
+ const data = hidden.data;
349
+ const hiddenSize = hidden.dims[2];
350
+ if (hiddenSize !== LOCAL_MODEL_DIMENSIONS) {
351
+ throw new Error(
352
+ `model produced ${hiddenSize} dimensions, expected ${LOCAL_MODEL_DIMENSIONS}`
353
+ );
354
+ }
355
+ return encodings.map((_, row) => {
356
+ const start = row * width * hiddenSize;
357
+ return normalize2(Array.from(data.subarray(start, start + hiddenSize)));
358
+ });
359
+ }
360
+ };
361
+ }
362
+ function truncate(encoding) {
363
+ if (encoding.ids.length <= MAX_TOKENS) return encoding;
364
+ const ids = [
365
+ ...encoding.ids.slice(0, MAX_TOKENS - 1),
366
+ encoding.ids[encoding.ids.length - 1]
367
+ ];
368
+ return { ids, attentionMask: ids.map(() => 1), tokenTypeIds: ids.map(() => 0) };
369
+ }
370
+ function normalize2(vector) {
371
+ const norm = Math.sqrt(vector.reduce((sum, value) => sum + value * value, 0));
372
+ return norm === 0 ? vector : vector.map((value) => value / norm);
373
+ }
374
+ function cosine(a, b) {
375
+ let sum = 0;
376
+ for (let i = 0; i < a.length; i += 1) sum += a[i] * b[i];
377
+ return sum;
378
+ }
379
+
380
+ // src/registry/localSemantic.ts
381
+ function isMediaVectorRow(value) {
382
+ if (!value || typeof value !== "object") return false;
383
+ const row = value;
384
+ return typeof row.id === "string" && typeof row.kind === "string" && typeof row.title === "string" && typeof row.description === "string" && Array.isArray(row.tags) && row.tags.every((tag) => typeof tag === "string") && typeof row.file === "string" && (row.duration === void 0 || typeof row.duration === "number" && Number.isFinite(row.duration) && row.duration >= 0) && (row.dimensions === void 0 || typeof row.dimensions === "object" && row.dimensions !== null && Number.isInteger(row.dimensions.width) && Number.isInteger(row.dimensions.height) && row.dimensions.width > 0 && row.dimensions.height > 0);
385
+ }
386
+ var CATALOG_ARTIFACT_TIMEOUT_MS = 3e4;
387
+ function localVectorDirectory() {
388
+ return process.env["HYPERFRAMES_CATALOG_ARTIFACT_DIR"] ?? join4(homedir4(), ".hyperframes", "catalog");
389
+ }
390
+ function vectorPairAgrees(fetched, artifactBasename) {
391
+ const meta = fetched.find(([file]) => file === `${artifactBasename}.json`)?.[1];
392
+ const bin = fetched.find(([file]) => file === `${artifactBasename}.bin`)?.[1];
393
+ if (!meta || !bin) return false;
394
+ try {
395
+ const parsed = JSON.parse(meta.toString("utf-8"));
396
+ if (parsed.dimensions !== LOCAL_MODEL_DIMENSIONS) return false;
397
+ if (artifactBasename === "media-vectors" && (!parsed.rows || parsed.rows.length !== (parsed.names?.length ?? -1) || parsed.rows.some(
398
+ (row, index) => !isMediaVectorRow(row) || row.id !== parsed.names?.[index]
399
+ ))) {
400
+ return false;
401
+ }
402
+ return bin.byteLength === (parsed.names?.length ?? -1) * LOCAL_MODEL_DIMENSIONS * 4;
403
+ } catch {
404
+ return false;
405
+ }
406
+ }
407
+ function vectorRevisionAgrees(fetched, expectedRevision, artifactBasename = "local-vectors") {
408
+ if (expectedRevision === void 0) return true;
409
+ const meta = fetched.find(([file]) => file === `${artifactBasename}.json`)?.[1];
410
+ if (!meta) return false;
411
+ try {
412
+ const parsed = JSON.parse(meta.toString("utf-8"));
413
+ return parsed.revision === expectedRevision;
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+ async function fetchLocalVectors(registryBaseUrl, options = {}) {
419
+ const directory = options.directory ?? localVectorDirectory();
420
+ const artifactBasename = options.artifactBasename ?? "local-vectors";
421
+ const base = registryBaseUrl.replace(/\/+$/, "");
422
+ try {
423
+ mkdirSync4(directory, { recursive: true, mode: 448 });
424
+ const fetched = [];
425
+ for (const file of [`${artifactBasename}.json`, `${artifactBasename}.bin`]) {
426
+ const response = await fetch(`${base}/catalog-artifact/${file}`, {
427
+ signal: AbortSignal.timeout(CATALOG_ARTIFACT_TIMEOUT_MS)
428
+ });
429
+ if (!response.ok) return false;
430
+ fetched.push([file, Buffer.from(await response.arrayBuffer())]);
431
+ }
432
+ if (!vectorPairAgrees(fetched, artifactBasename) || !vectorRevisionAgrees(fetched, options.expectedRevision, artifactBasename)) {
433
+ return false;
434
+ }
435
+ for (const [file, bytes] of fetched) {
436
+ writeFileSync3(join4(directory, file), bytes, { mode: 384 });
437
+ }
438
+ return artifactBasename === "media-vectors" ? hasMediaVectors(directory) : hasLocalVectors(directory);
439
+ } catch {
440
+ return false;
441
+ }
442
+ }
443
+ function hasLocalVectors(directory = localVectorDirectory()) {
444
+ return existsSync4(join4(directory, "local-vectors.bin")) && existsSync4(join4(directory, "local-vectors.json"));
445
+ }
446
+ function hasMediaVectors(directory = localVectorDirectory()) {
447
+ return existsSync4(join4(directory, "media-vectors.bin")) && existsSync4(join4(directory, "media-vectors.json"));
448
+ }
449
+ function loadLocalVectors(directory = localVectorDirectory()) {
450
+ const meta = JSON.parse(
451
+ readFileSync5(join4(directory, "local-vectors.json"), "utf-8")
452
+ );
453
+ const buffer = readFileSync5(join4(directory, "local-vectors.bin"));
454
+ const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
455
+ const expected = meta.names.length * meta.dimensions;
456
+ if (vectors.length !== expected) {
457
+ throw new Error(`local vectors hold ${vectors.length} floats, expected ${expected}`);
458
+ }
459
+ if (meta.dimensions !== LOCAL_MODEL_DIMENSIONS) {
460
+ throw new Error(
461
+ `local vectors are ${meta.dimensions}-dimension, model produces ${LOCAL_MODEL_DIMENSIONS}`
462
+ );
463
+ }
464
+ return { names: meta.names, dimensions: meta.dimensions, vectors };
465
+ }
466
+ function loadMediaVectors(directory = localVectorDirectory()) {
467
+ const meta = JSON.parse(
468
+ readFileSync5(join4(directory, "media-vectors.json"), "utf-8")
469
+ );
470
+ const buffer = readFileSync5(join4(directory, "media-vectors.bin"));
471
+ const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
472
+ const rows = meta.rows ?? [];
473
+ if (rows.length !== meta.names.length) {
474
+ throw new Error(`media vectors hold ${rows.length} rows, expected ${meta.names.length}`);
475
+ }
476
+ if (rows.some((row, index) => !isMediaVectorRow(row) || row.id !== meta.names[index])) {
477
+ throw new Error("media vector row order does not match media vector names");
478
+ }
479
+ if (vectors.length !== rows.length * meta.dimensions) {
480
+ throw new Error(
481
+ `media vectors hold ${vectors.length} floats, expected ${rows.length * meta.dimensions}`
482
+ );
483
+ }
484
+ if (meta.dimensions !== LOCAL_MODEL_DIMENSIONS) {
485
+ throw new Error(
486
+ `media vectors are ${meta.dimensions}-dimension, model produces ${LOCAL_MODEL_DIMENSIONS}`
487
+ );
488
+ }
489
+ return { names: meta.names, dimensions: meta.dimensions, vectors, rows };
490
+ }
491
+ function localVectorNames(directory = localVectorDirectory()) {
492
+ if (!hasLocalVectors(directory)) return [];
493
+ try {
494
+ const meta = JSON.parse(readFileSync5(join4(directory, "local-vectors.json"), "utf-8"));
495
+ return meta.names ?? [];
496
+ } catch {
497
+ return [];
498
+ }
499
+ }
500
+ function cachedLocalVectorRevision(directory = localVectorDirectory()) {
501
+ if (!hasLocalVectors(directory)) return void 0;
502
+ try {
503
+ const meta = JSON.parse(readFileSync5(join4(directory, "local-vectors.json"), "utf-8"));
504
+ return typeof meta.revision === "string" ? meta.revision : void 0;
505
+ } catch {
506
+ return void 0;
507
+ }
508
+ }
509
+ async function localSemanticRanking(query, directory = localVectorDirectory()) {
510
+ if (!isLocalModelReady() || !hasLocalVectors(directory)) return null;
511
+ const set = loadLocalVectors(directory);
512
+ const embedder = await loadLocalEmbedder();
513
+ const [queryVector] = await embedder.embed([query], { isQuery: true });
514
+ if (!queryVector) return null;
515
+ const scored = set.names.map((name, row) => {
516
+ const start = row * set.dimensions;
517
+ return {
518
+ name,
519
+ score: cosine(queryVector, Array.from(set.vectors.subarray(start, start + set.dimensions)))
520
+ };
521
+ });
522
+ scored.sort((a, b) => b.score - a.score || b.name.localeCompare(a.name));
523
+ return scored;
524
+ }
525
+ async function mediaSemanticRanking(query, directory = localVectorDirectory()) {
526
+ if (!isLocalModelReady() || !hasMediaVectors(directory)) return null;
527
+ const set = loadMediaVectors(directory);
528
+ const embedder = await loadLocalEmbedder();
529
+ const [queryVector] = await embedder.embed([query], { isQuery: true });
530
+ if (!queryVector) return null;
531
+ return set.rows.map((row, index) => ({
532
+ row,
533
+ score: cosine(
534
+ queryVector,
535
+ Array.from(set.vectors.subarray(index * set.dimensions, (index + 1) * set.dimensions))
536
+ )
537
+ })).sort((a, b) => b.score - a.score || b.row.id.localeCompare(a.row.id));
538
+ }
539
+ export {
540
+ cachedLocalVectorRevision,
541
+ fetchLocalVectors,
542
+ hasLocalVectors,
543
+ isMediaVectorRow,
544
+ localSemanticRanking,
545
+ localVectorNames,
546
+ mediaSemanticRanking,
547
+ vectorPairAgrees
548
+ };
@@ -16,7 +16,7 @@ Run commands as `npx hyperframes ...` unless project instructions provide a wrap
16
16
  ## Development loop
17
17
 
18
18
  1. **Scaffold:** `npx hyperframes init <project>` (centered blank). Or capture a site. Pass `--example=<name>` only to start from a named example.
19
- 2. **Find the move:** before authoring motion by hand, search for a primitive that already does it: `npx hyperframes catalog --query "reveal a headline one line at a time"`. Ask for the effect you want rather than the mechanism you have in mind. Install with `npx hyperframes add <name>` (see `/hyperframes-registry`). Author by hand only once nothing fits.
19
+ 2. **Find the move:** if the request names an asset, sound, image, voice or fast visual edit, resolve it through `/media-use` before proposing a plan. Otherwise, before authoring motion by hand, search for a primitive that already does it: `npx hyperframes catalog --query "reveal a headline one line at a time"`. Ask for the effect you want rather than the mechanism you have in mind. Install with `npx hyperframes add <name>` (see `/hyperframes-registry`). Author by hand only once nothing fits.
20
20
  3. **Author:** write the composition using `/hyperframes-core`. To know what is on a project's timeline (tracks, clips, starts, ends, what plays), run `npx hyperframes timeline --json` instead of reading `index.html` and every sub-composition file: nested rows carry absolute main-timeline `absStart`/`absEnd` and their owning `file`, not just their local, per-sub-composition time. Prefer `--json` over the text form; it costs fewer tokens for the same or better correctness. See `references/upgrade-info-misc.md` for one-liners that answer common questions without reading the whole output.
21
21
  4. **Get fast feedback while editing:** run `npx hyperframes lint` after the first HTML pass and after structural changes.
22
22
  5. **Run the final gate:** run `npx hyperframes check`; it reruns lint before opening the browser. Do not prepend a redundant standalone lint invocation. Add `--snapshots` for annotated overview frames and finding crops.
@@ -36,7 +36,7 @@ Run commands as `npx hyperframes ...` unless project instructions provide a wrap
36
36
  - For fade-in/fade-out, crossfade, track gain, volume automation, ducking,
37
37
  voiceover carve, or FX on placed audio, read `/hyperframes-audio`. Load core
38
38
  alongside it when clip placement or picture timing also changes.
39
- - Use `/media-use` only to source/generate media or preprocess a derived asset.
39
+ - A request naming an asset, sound, image, voice or fast visual edit resolves through `/media-use` before a plan is proposed.
40
40
  Copy creator edit markup from `/hyperframes-core` → `references/creator-editing-recipes.md`.
41
41
 
42
42
  ```bash
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: media-use
3
+ description: Agent Media OS, the single skill for every media need in a HyperFrames project. Resolve BGM, SFX, image, icon, brand logo, voice, color grade, or LUT into a frozen local file or paste-ready block + ledger record (one verb, `resolve`); generate via TTS / music / image models when the catalog misses; produce voiceover, transcription, captions, and background removal through one shared audio engine; operate on media (cut / reframe / transform); and reuse assets across projects. Also use for vague feedback that real footage looks dark, flat, boring, should feel retro/camcorder/print/ASCII, needs privacy, or needs a media reveal.
4
+ ---
5
+
6
+ # media-use
7
+
8
+ The media OS for HyperFrames: resolve · generate · operate · remember — every media type, one skill, zero context noise.
9
+
10
+ First run: install and sign in to the `heygen` CLI (the free-usage path), then verify with `npx hyperframes media-use resolve --doctor`. Setup and providers: `references/setup-providers.md`.
11
+
12
+ ## Resolve — the one verb
13
+
14
+ ```bash
15
+ npx hyperframes media-use resolve --type <type> --intent "<description>" --project <dir>
16
+ ```
17
+
18
+ Returns one line: `resolved <id> → <path> (<type>, <metadata>)`. All search noise stays on disk.
19
+
20
+ | Type | One-line intent |
21
+ | ------- | ----------------------------------------------------------------------------------- |
22
+ | `bgm` | background music (HeyGen catalog, 10k+ tracks) |
23
+ | `sfx` | sound effects (bundled 19-file library + catalog) |
24
+ | `image` | photos, backgrounds (HeyGen asset search, 75k+ vectors) |
25
+ | `icon` | icons, symbols (transparent) |
26
+ | `logo` | official brand marks (svgl → simple-icons → GitHub avatar → favicon; never redrawn) |
27
+ | `voice` | TTS voiceover (HeyGen free-usage path; optional local Kokoro) |
28
+ | `grade` | measured correction candidate; broad polish/stylization follows Media Treatments |
29
+ | `lut` | user-provided or explicitly chosen reusable validated `.cube` file |
30
+
31
+ Before resolving fresh, list reusable candidates with `--candidates` and judge fit yourself — reuse rules, all flags, ingest (`--from`), and adopt are in `references/resolve.md`.
32
+
33
+ ## Treat broad visual feedback as media intent
34
+
35
+ When a user explicitly asks to fix, polish, stylize, obscure, emphasize, or
36
+ reveal photographic media, read `references/media-treatments.md` even if they
37
+ do not name color grading or an effect. Inspect the real `<img>`/`<video>`,
38
+ choose one primary intent, then use deterministic persistence and verification.
39
+ Use a matching recipe as an optional tested seed, or inspect
40
+ `hyperframes media-treatment --capabilities --json`, then request one relevant
41
+ family/effect with `--capability <id>` and assemble a custom treatment from
42
+ canonical controls. Never load `--all` for ordinary authoring. A treatment may
43
+ compose correction, a preset, finishing, compatible shader effects, supported
44
+ keyframes, and optional Registry overlays. Add only source-justified bounded
45
+ tuning and compatible parts, never effects merely to make the result look more
46
+ sophisticated. Persist the final combined payload with
47
+ `hyperframes media-treatment`.
48
+
49
+ Use one progressively escalating workflow. For video, inspect one labeled
50
+ early/middle/late contact sheet rather than reading frames separately. Apply one
51
+ candidate and inspect one after-sheet for ordinary correction or polish.
52
+ Escalate to individual frames or moving draft evidence only when the result is
53
+ ambiguous, temporal, stylized, LUT-based, HDR/LOG-sensitive, private, or
54
+ brand-critical.
55
+
56
+ For ordinary correction or polish, persist the final treatment's
57
+ preset/adjustment JSON.
58
+ Do not generate a `.cube` LUT merely to encode exposure, shadows, contrast, or
59
+ warmth. Use a LUT only when the user supplies one or the selected treatment
60
+ explicitly owns one. `resolve --type grade --for ... --analyze` is measurement
61
+ evidence, not permission to replace the chosen treatment with a generated LUT.
62
+ Do not recreate supported vignette, grain, blur, pixelate, color, or treatment
63
+ effects with CSS/SVG overlays; that bypasses Studio controls and the canonical
64
+ preview/render shader path.
65
+
66
+ ## Be proactive — run a media opportunity pass
67
+
68
+ The human usually can't tell which media would lift the piece. You can. When you build or review a composition, do **one** grounded scan and then **ask once** — don't silently add, and don't nag per asset.
69
+
70
+ Surface an opportunity only when a concrete signal is present:
71
+
72
+ | Signal detected | Offer |
73
+ | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
74
+ | On-screen text / a script with no voiceover | TTS voiceover (audio engine) |
75
+ | Emoji or a `<div>` styled as an icon | resolve real `icon`s |
76
+ | Image that is a placeholder, tiny, or upscaled-looking | a better `image` (and/or upscale — see `references/operations.md`) |
77
+ | Hard scene cuts / transitions with no sound | transition `sfx` |
78
+ | A piece over ~10s with no music bed | `bgm` |
79
+ | Footage that reads under/over-exposed or color-cast | a corrective grade (inspect it with `hyperframes media-treatment --selector '#hero' --analyze --json`) |
80
+ | Photographic media that feels visually flat or off-topic | one specific source-appropriate preset or custom treatment, with the intended target named |
81
+ | A meaningful media entrance/reveal that feels static | one supported seek-safe treatment animation; preserve color unless the request also justifies a preset |
82
+
83
+ Rules that keep this a help, not nagware: **grounded, not generic** (no signal → no suggestion); **opinionated + concrete** (propose the specific fix with defaults chosen — the human approves **all / some / none**); **once per project** (one consolidated ask; respect "leave it"); **surface, never silently mutate** (color grades especially: propose and preview — a gray-world "correction" ruins an intentional sunset or neon look).
84
+
85
+ ## Where to look — read only the file your task needs
86
+
87
+ | Task | Read |
88
+ | ------------------------------------------------------------------------- | -------------------------------- |
89
+ | resolve / reuse / adopt / ingest, flags, cascade, inventory | `references/resolve.md` |
90
+ | color grading, LUTs, smart grade (`--for`), grade-compare | `references/grading.md` |
91
+ | voiceover / TTS, music, SFX, captions, transcription (audio engine) | `references/audio.md` |
92
+ | cut / reframe / transform existing media, exact error diffusion, HEVC | `references/operations.md` |
93
+ | source-aware creative treatments, realtime effects, overlays, reveals | `references/media-treatments.md` |
94
+ | install + auth, provider table, RAM ladders, `--local-only`, `--provider` | `references/setup-providers.md` |
95
+ | remembered preferences + frozen recipes (user memory) | `references/memory.md` |
96
+ | ownership matrix, usage stats, telemetry, privacy (maintainer-facing) | `references/meta.md` |