feat: add asset preferences, video research, and Remotion ad assets
- Add thumbs-down feedback modal and preference API endpoint - Add AI UGC video platforms research doc - Add ReflectAd Remotion composition with public flow assets - Add gemini-ad-designer and poster-ad-designer pipeline skills - Add research_reflect_v1.1 pipeline script Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+48
-3
@@ -12,6 +12,29 @@ interface ScannedFile {
|
||||
metadata: string | null;
|
||||
}
|
||||
|
||||
/** Truncate a JSON string to maxLen while keeping it valid JSON. */
|
||||
function safeJsonTruncate(json: string, maxLen: number): string {
|
||||
if (json.length <= maxLen) return json;
|
||||
try {
|
||||
const parsed = JSON.parse(json);
|
||||
// Try to produce a shorter version by re-stringifying with key limits
|
||||
const trimmed = JSON.stringify(parsed, (_key, value) => {
|
||||
if (typeof value === "string" && value.length > 200) {
|
||||
return value.slice(0, 200) + "…";
|
||||
}
|
||||
if (Array.isArray(value) && value.length > 5) {
|
||||
return value.slice(0, 5);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
if (trimmed.length <= maxLen) return trimmed;
|
||||
// Still too long — return a minimal summary
|
||||
return JSON.stringify({ _truncated: true, _originalLength: json.length });
|
||||
} catch {
|
||||
return JSON.stringify({ _truncated: true, _originalLength: json.length });
|
||||
}
|
||||
}
|
||||
|
||||
const FORMAT_TO_TYPE: Record<string, string> = {
|
||||
png: "image",
|
||||
jpg: "image",
|
||||
@@ -65,13 +88,13 @@ function loadMetadata(fullPath: string, format: string): string | null {
|
||||
if (Array.isArray(parsed)) {
|
||||
return JSON.stringify({ captions: parsed.slice(0, 3), totalVariations: parsed.length });
|
||||
}
|
||||
return content.slice(0, 2000);
|
||||
return safeJsonTruncate(content, 2000);
|
||||
}
|
||||
|
||||
// For media files, look for adjacent JSON with same name
|
||||
const jsonPath = fullPath.replace(/\.[^.]+$/, ".json");
|
||||
if (existsSync(jsonPath)) {
|
||||
return readFileSync(jsonPath, "utf-8").slice(0, 2000);
|
||||
return safeJsonTruncate(readFileSync(jsonPath, "utf-8"), 2000);
|
||||
}
|
||||
|
||||
// Look for manifest in same directory
|
||||
@@ -85,7 +108,10 @@ function loadMetadata(fullPath: string, format: string): string | null {
|
||||
const entry = manifest.find((e: { fileName?: string; file?: string }) =>
|
||||
e.fileName === fileName || e.file === fileName
|
||||
);
|
||||
if (entry) return JSON.stringify(entry);
|
||||
if (entry) {
|
||||
// Ensure style field is preserved in metadata
|
||||
return JSON.stringify(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +148,25 @@ function scanDirectory(dir: string, baseDir: string): ScannedFile[] {
|
||||
const ext = path.extname(entry).toLowerCase().slice(1);
|
||||
if (!ext || ext === "gitkeep") continue;
|
||||
|
||||
// Only ingest deliverable files — skip source/build artifacts
|
||||
const ASSET_EXTENSIONS = new Set([
|
||||
"png", "jpg", "jpeg", "webp", "gif", // images
|
||||
"mp4", "webm", // videos
|
||||
]);
|
||||
const CONTENT_EXTENSIONS = new Set([
|
||||
"json", "md", "txt", // copy/scripts/research
|
||||
]);
|
||||
// Skip HTML source files, render scripts, and build tools
|
||||
if (!ASSET_EXTENSIONS.has(ext) && !CONTENT_EXTENSIONS.has(ext)) continue;
|
||||
// Skip known build/tool artifacts
|
||||
const SKIP_FILES = new Set([
|
||||
"tavily_search.mjs", "render_posters.mjs", "design_philosophy.md",
|
||||
]);
|
||||
if (SKIP_FILES.has(entry)) continue;
|
||||
// Skip HTML source files in ads/ (they're build artifacts, not deliverables)
|
||||
const relativePath0 = path.relative(baseDir, fullPath);
|
||||
if (ext === "html" && relativePath0.includes("/ads/")) continue;
|
||||
|
||||
const relativePath = path.relative(baseDir, fullPath);
|
||||
const type = inferTypeFromPath(relativePath, ext);
|
||||
const metadata = loadMetadata(fullPath, ext);
|
||||
|
||||
Reference in New Issue
Block a user