@contractspec/lib.video-gen 1.44.0 → 1.46.0

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 (61) hide show
  1. package/README.md +37 -0
  2. package/dist/browser/docs/rendering.docblock.js +4 -4
  3. package/dist/browser/docs/video-gen.docblock.js +1 -1
  4. package/dist/browser/generators/index.js +601 -192
  5. package/dist/browser/generators/scene-planner.js +527 -23
  6. package/dist/browser/generators/script-generator.js +528 -18
  7. package/dist/browser/generators/video-generator.js +601 -191
  8. package/dist/browser/i18n/catalogs/en.js +160 -0
  9. package/dist/browser/i18n/catalogs/es.js +160 -0
  10. package/dist/browser/i18n/catalogs/fr.js +160 -0
  11. package/dist/browser/i18n/catalogs/index.js +462 -0
  12. package/dist/browser/i18n/index.js +534 -0
  13. package/dist/browser/i18n/keys.js +54 -0
  14. package/dist/browser/i18n/locale.js +21 -0
  15. package/dist/browser/i18n/messages.js +474 -0
  16. package/dist/browser/index.js +601 -192
  17. package/dist/docs/rendering.docblock.js +4 -4
  18. package/dist/docs/video-gen.docblock.js +1 -1
  19. package/dist/generators/index.d.ts +0 -2
  20. package/dist/generators/index.js +601 -192
  21. package/dist/generators/scene-planner.d.ts +2 -0
  22. package/dist/generators/scene-planner.js +527 -23
  23. package/dist/generators/script-generator.d.ts +2 -0
  24. package/dist/generators/script-generator.js +528 -18
  25. package/dist/generators/video-generator.d.ts +6 -5
  26. package/dist/generators/video-generator.js +601 -191
  27. package/dist/i18n/catalogs/en.d.ts +8 -0
  28. package/dist/i18n/catalogs/en.js +155 -0
  29. package/dist/i18n/catalogs/es.d.ts +6 -0
  30. package/dist/i18n/catalogs/es.js +155 -0
  31. package/dist/i18n/catalogs/fr.d.ts +6 -0
  32. package/dist/i18n/catalogs/fr.js +155 -0
  33. package/dist/i18n/catalogs/index.d.ts +8 -0
  34. package/dist/i18n/catalogs/index.js +457 -0
  35. package/dist/i18n/i18n.test.d.ts +1 -0
  36. package/dist/i18n/index.d.ts +22 -0
  37. package/dist/i18n/index.js +529 -0
  38. package/dist/i18n/keys.d.ts +116 -0
  39. package/dist/i18n/keys.js +49 -0
  40. package/dist/i18n/locale.d.ts +8 -0
  41. package/dist/i18n/locale.js +16 -0
  42. package/dist/i18n/messages.d.ts +14 -0
  43. package/dist/i18n/messages.js +469 -0
  44. package/dist/index.js +601 -192
  45. package/dist/node/docs/rendering.docblock.js +4 -4
  46. package/dist/node/docs/video-gen.docblock.js +1 -1
  47. package/dist/node/generators/index.js +601 -192
  48. package/dist/node/generators/scene-planner.js +527 -23
  49. package/dist/node/generators/script-generator.js +528 -18
  50. package/dist/node/generators/video-generator.js +601 -191
  51. package/dist/node/i18n/catalogs/en.js +155 -0
  52. package/dist/node/i18n/catalogs/es.js +155 -0
  53. package/dist/node/i18n/catalogs/fr.js +155 -0
  54. package/dist/node/i18n/catalogs/index.js +457 -0
  55. package/dist/node/i18n/index.js +529 -0
  56. package/dist/node/i18n/keys.js +49 -0
  57. package/dist/node/i18n/locale.js +16 -0
  58. package/dist/node/i18n/messages.js +469 -0
  59. package/dist/node/index.js +601 -192
  60. package/dist/types.d.ts +14 -3
  61. package/package.json +149 -6
@@ -1120,17 +1120,528 @@ var TerminalDemo = ({
1120
1120
  ]
1121
1121
  }, undefined, true, undefined, this);
1122
1122
  };
1123
+ // src/i18n/catalogs/en.ts
1124
+ import { defineTranslation } from "@contractspec/lib.contracts-spec/translations";
1125
+ var enMessages = defineTranslation({
1126
+ meta: {
1127
+ key: "video-gen.messages",
1128
+ version: "1.0.0",
1129
+ domain: "video-gen",
1130
+ description: "All user-facing, LLM-facing, and developer-facing strings for the video-gen package",
1131
+ owners: ["platform"],
1132
+ stability: "experimental"
1133
+ },
1134
+ locale: "en",
1135
+ fallback: "en",
1136
+ messages: {
1137
+ "prompt.script.system": {
1138
+ value: `You are a video narration script writer.
1139
+ Write a narration script for a short video (30-60 seconds).
1140
+ {styleGuide}
1141
+
1142
+ Return JSON with shape:
1143
+ {
1144
+ "segments": [{ "sceneId": string, "text": string }],
1145
+ "fullText": string
1146
+ }
1147
+
1148
+ Scene IDs should be: "intro", "problems", "solutions", "metrics", "cta".
1149
+ Only include segments that are relevant to the brief content.`,
1150
+ description: "Script generator LLM system prompt",
1151
+ placeholders: [{ name: "styleGuide", type: "string" }]
1152
+ },
1153
+ "prompt.scenePlanner.system": {
1154
+ value: `You are a video scene planner for ContractSpec marketing/documentation videos.
1155
+ Given a content brief, break it into video scenes.
1156
+
1157
+ Each scene must have:
1158
+ - compositionId: one of "ApiOverview", "SocialClip", "TerminalDemo"
1159
+ - props: the input props for that composition (see type definitions)
1160
+ - durationInFrames: duration at {fps}fps
1161
+ - narrationText: what the narrator says during this scene
1162
+
1163
+ Return a JSON object with shape:
1164
+ {
1165
+ "scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
1166
+ "narrationScript": string
1167
+ }
1168
+
1169
+ Keep the total duration around {targetSeconds} seconds.
1170
+ Prioritize clarity and pacing. Each scene should communicate one idea.`,
1171
+ description: "Scene planner LLM system prompt",
1172
+ placeholders: [
1173
+ { name: "fps", type: "number" },
1174
+ { name: "targetSeconds", type: "number" }
1175
+ ]
1176
+ },
1177
+ "prompt.style.professional": {
1178
+ value: "Use a clear, authoritative, professional tone. Be concise and direct.",
1179
+ description: "Style guide for professional narration"
1180
+ },
1181
+ "prompt.style.casual": {
1182
+ value: "Use a friendly, conversational tone. Be approachable and relatable.",
1183
+ description: "Style guide for casual narration"
1184
+ },
1185
+ "prompt.style.technical": {
1186
+ value: "Use precise technical language. Be detailed and accurate.",
1187
+ description: "Style guide for technical narration"
1188
+ },
1189
+ "script.segment.challenge": {
1190
+ value: "The challenge: {content}",
1191
+ description: "Narration segment prefix for problems",
1192
+ placeholders: [{ name: "content", type: "string" }]
1193
+ },
1194
+ "script.segment.solution": {
1195
+ value: "The solution: {content}",
1196
+ description: "Narration segment prefix for solutions",
1197
+ placeholders: [{ name: "content", type: "string" }]
1198
+ },
1199
+ "script.segment.results": {
1200
+ value: "The results: {content}",
1201
+ description: "Narration segment prefix for metrics",
1202
+ placeholders: [{ name: "content", type: "string" }]
1203
+ },
1204
+ "scene.cta.default": {
1205
+ value: "Learn more",
1206
+ description: "Default call-to-action text for scenes"
1207
+ },
1208
+ "scene.hook.problem": {
1209
+ value: "The Problem",
1210
+ description: "Scene hook label for problem statement"
1211
+ },
1212
+ "scene.narration.problem": {
1213
+ value: "The problem: {content}",
1214
+ description: "Scene narration for problem statement",
1215
+ placeholders: [{ name: "content", type: "string" }]
1216
+ },
1217
+ "scene.hook.solution": {
1218
+ value: "The Solution",
1219
+ description: "Scene hook label for solution"
1220
+ },
1221
+ "scene.narration.solution": {
1222
+ value: "The solution: {content}",
1223
+ description: "Scene narration for solution",
1224
+ placeholders: [{ name: "content", type: "string" }]
1225
+ },
1226
+ "scene.hook.results": {
1227
+ value: "Results",
1228
+ description: "Scene hook label for results/metrics"
1229
+ },
1230
+ "composition.apiOverview.generates": {
1231
+ value: "Generates:",
1232
+ description: "ApiOverview heading for generated outputs"
1233
+ },
1234
+ "composition.apiOverview.tagline": {
1235
+ value: "One spec. Every surface.",
1236
+ description: "ApiOverview default tagline"
1237
+ },
1238
+ "composition.apiOverview.output.rest": {
1239
+ value: "REST Endpoint",
1240
+ description: "Generated output label: REST"
1241
+ },
1242
+ "composition.apiOverview.output.graphql": {
1243
+ value: "GraphQL Mutation",
1244
+ description: "Generated output label: GraphQL"
1245
+ },
1246
+ "composition.apiOverview.output.prisma": {
1247
+ value: "Prisma Model",
1248
+ description: "Generated output label: Prisma"
1249
+ },
1250
+ "composition.apiOverview.output.typescript": {
1251
+ value: "TypeScript SDK",
1252
+ description: "Generated output label: TypeScript SDK"
1253
+ },
1254
+ "composition.apiOverview.output.mcp": {
1255
+ value: "MCP Tool",
1256
+ description: "Generated output label: MCP Tool"
1257
+ },
1258
+ "composition.apiOverview.output.openapi": {
1259
+ value: "OpenAPI Spec",
1260
+ description: "Generated output label: OpenAPI"
1261
+ },
1262
+ "composition.socialClip.cta": {
1263
+ value: "Learn more",
1264
+ description: "SocialClip default CTA"
1265
+ },
1266
+ "composition.terminal.title": {
1267
+ value: "Terminal",
1268
+ description: "TerminalDemo default window title"
1269
+ }
1270
+ }
1271
+ });
1272
+
1273
+ // src/i18n/catalogs/fr.ts
1274
+ import { defineTranslation as defineTranslation2 } from "@contractspec/lib.contracts-spec/translations";
1275
+ var frMessages = defineTranslation2({
1276
+ meta: {
1277
+ key: "video-gen.messages",
1278
+ version: "1.0.0",
1279
+ domain: "video-gen",
1280
+ description: "French translations for the video-gen package",
1281
+ owners: ["platform"],
1282
+ stability: "experimental"
1283
+ },
1284
+ locale: "fr",
1285
+ fallback: "en",
1286
+ messages: {
1287
+ "prompt.script.system": {
1288
+ value: `Vous êtes un rédacteur de scripts de narration vidéo.
1289
+ Écrivez un script de narration pour une courte vidéo (30-60 secondes).
1290
+ {styleGuide}
1291
+
1292
+ Retournez du JSON avec la forme :
1293
+ {
1294
+ "segments": [{ "sceneId": string, "text": string }],
1295
+ "fullText": string
1296
+ }
1297
+
1298
+ Les identifiants de scène doivent être : "intro", "problems", "solutions", "metrics", "cta".
1299
+ N'incluez que les segments pertinents au brief.`,
1300
+ description: "Script generator LLM system prompt",
1301
+ placeholders: [{ name: "styleGuide", type: "string" }]
1302
+ },
1303
+ "prompt.scenePlanner.system": {
1304
+ value: `Vous êtes un planificateur de scènes vidéo pour les vidéos marketing/documentation de ContractSpec.
1305
+ À partir d'un brief, décomposez-le en scènes vidéo.
1306
+
1307
+ Chaque scène doit avoir :
1308
+ - compositionId : un parmi "ApiOverview", "SocialClip", "TerminalDemo"
1309
+ - props : les propriétés d'entrée de cette composition
1310
+ - durationInFrames : durée à {fps} fps
1311
+ - narrationText : ce que le narrateur dit pendant cette scène
1312
+
1313
+ Retournez un objet JSON avec la forme :
1314
+ {
1315
+ "scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
1316
+ "narrationScript": string
1317
+ }
1318
+
1319
+ Gardez la durée totale autour de {targetSeconds} secondes.
1320
+ Privilégiez la clarté et le rythme. Chaque scène doit communiquer une idée.`,
1321
+ description: "Scene planner LLM system prompt",
1322
+ placeholders: [
1323
+ { name: "fps", type: "number" },
1324
+ { name: "targetSeconds", type: "number" }
1325
+ ]
1326
+ },
1327
+ "prompt.style.professional": {
1328
+ value: "Utilisez un ton clair, autoritaire et professionnel. Soyez concis et direct.",
1329
+ description: "Style guide for professional narration"
1330
+ },
1331
+ "prompt.style.casual": {
1332
+ value: "Utilisez un ton amical et conversationnel. Soyez accessible et proche.",
1333
+ description: "Style guide for casual narration"
1334
+ },
1335
+ "prompt.style.technical": {
1336
+ value: "Utilisez un langage technique précis. Soyez détaillé et exact.",
1337
+ description: "Style guide for technical narration"
1338
+ },
1339
+ "script.segment.challenge": {
1340
+ value: "Le défi : {content}",
1341
+ description: "Narration segment prefix for problems",
1342
+ placeholders: [{ name: "content", type: "string" }]
1343
+ },
1344
+ "script.segment.solution": {
1345
+ value: "La solution : {content}",
1346
+ description: "Narration segment prefix for solutions",
1347
+ placeholders: [{ name: "content", type: "string" }]
1348
+ },
1349
+ "script.segment.results": {
1350
+ value: "Les résultats : {content}",
1351
+ description: "Narration segment prefix for metrics",
1352
+ placeholders: [{ name: "content", type: "string" }]
1353
+ },
1354
+ "scene.cta.default": {
1355
+ value: "En savoir plus",
1356
+ description: "Default call-to-action text for scenes"
1357
+ },
1358
+ "scene.hook.problem": {
1359
+ value: "Le problème",
1360
+ description: "Scene hook label for problem statement"
1361
+ },
1362
+ "scene.narration.problem": {
1363
+ value: "Le problème : {content}",
1364
+ description: "Scene narration for problem statement",
1365
+ placeholders: [{ name: "content", type: "string" }]
1366
+ },
1367
+ "scene.hook.solution": {
1368
+ value: "La solution",
1369
+ description: "Scene hook label for solution"
1370
+ },
1371
+ "scene.narration.solution": {
1372
+ value: "La solution : {content}",
1373
+ description: "Scene narration for solution",
1374
+ placeholders: [{ name: "content", type: "string" }]
1375
+ },
1376
+ "scene.hook.results": {
1377
+ value: "Résultats",
1378
+ description: "Scene hook label for results/metrics"
1379
+ },
1380
+ "composition.apiOverview.generates": {
1381
+ value: "Génère :",
1382
+ description: "ApiOverview heading for generated outputs"
1383
+ },
1384
+ "composition.apiOverview.tagline": {
1385
+ value: "Un spec. Toutes les surfaces.",
1386
+ description: "ApiOverview default tagline"
1387
+ },
1388
+ "composition.apiOverview.output.rest": {
1389
+ value: "Endpoint REST",
1390
+ description: "Generated output label: REST"
1391
+ },
1392
+ "composition.apiOverview.output.graphql": {
1393
+ value: "Mutation GraphQL",
1394
+ description: "Generated output label: GraphQL"
1395
+ },
1396
+ "composition.apiOverview.output.prisma": {
1397
+ value: "Modèle Prisma",
1398
+ description: "Generated output label: Prisma"
1399
+ },
1400
+ "composition.apiOverview.output.typescript": {
1401
+ value: "SDK TypeScript",
1402
+ description: "Generated output label: TypeScript SDK"
1403
+ },
1404
+ "composition.apiOverview.output.mcp": {
1405
+ value: "Outil MCP",
1406
+ description: "Generated output label: MCP Tool"
1407
+ },
1408
+ "composition.apiOverview.output.openapi": {
1409
+ value: "Spec OpenAPI",
1410
+ description: "Generated output label: OpenAPI"
1411
+ },
1412
+ "composition.socialClip.cta": {
1413
+ value: "En savoir plus",
1414
+ description: "SocialClip default CTA"
1415
+ },
1416
+ "composition.terminal.title": {
1417
+ value: "Terminal",
1418
+ description: "TerminalDemo default window title"
1419
+ }
1420
+ }
1421
+ });
1422
+
1423
+ // src/i18n/catalogs/es.ts
1424
+ import { defineTranslation as defineTranslation3 } from "@contractspec/lib.contracts-spec/translations";
1425
+ var esMessages = defineTranslation3({
1426
+ meta: {
1427
+ key: "video-gen.messages",
1428
+ version: "1.0.0",
1429
+ domain: "video-gen",
1430
+ description: "Spanish translations for the video-gen package",
1431
+ owners: ["platform"],
1432
+ stability: "experimental"
1433
+ },
1434
+ locale: "es",
1435
+ fallback: "en",
1436
+ messages: {
1437
+ "prompt.script.system": {
1438
+ value: `Eres un redactor de guiones de narración para vídeo.
1439
+ Escribe un guión de narración para un vídeo corto (30-60 segundos).
1440
+ {styleGuide}
1441
+
1442
+ Devuelve JSON con la forma:
1443
+ {
1444
+ "segments": [{ "sceneId": string, "text": string }],
1445
+ "fullText": string
1446
+ }
1447
+
1448
+ Los identificadores de escena deben ser: "intro", "problems", "solutions", "metrics", "cta".
1449
+ Incluye solo los segmentos relevantes para el brief.`,
1450
+ description: "Script generator LLM system prompt",
1451
+ placeholders: [{ name: "styleGuide", type: "string" }]
1452
+ },
1453
+ "prompt.scenePlanner.system": {
1454
+ value: `Eres un planificador de escenas de vídeo para vídeos de marketing/documentación de ContractSpec.
1455
+ Dado un brief de contenido, divídelo en escenas de vídeo.
1456
+
1457
+ Cada escena debe tener:
1458
+ - compositionId: uno de "ApiOverview", "SocialClip", "TerminalDemo"
1459
+ - props: las propiedades de entrada de esa composición
1460
+ - durationInFrames: duración a {fps} fps
1461
+ - narrationText: lo que dice el narrador durante esta escena
1462
+
1463
+ Devuelve un objeto JSON con la forma:
1464
+ {
1465
+ "scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
1466
+ "narrationScript": string
1467
+ }
1468
+
1469
+ Mantén la duración total alrededor de {targetSeconds} segundos.
1470
+ Prioriza la claridad y el ritmo. Cada escena debe comunicar una idea.`,
1471
+ description: "Scene planner LLM system prompt",
1472
+ placeholders: [
1473
+ { name: "fps", type: "number" },
1474
+ { name: "targetSeconds", type: "number" }
1475
+ ]
1476
+ },
1477
+ "prompt.style.professional": {
1478
+ value: "Usa un tono claro, autoritario y profesional. Sé conciso y directo.",
1479
+ description: "Style guide for professional narration"
1480
+ },
1481
+ "prompt.style.casual": {
1482
+ value: "Usa un tono amigable y conversacional. Sé accesible y cercano.",
1483
+ description: "Style guide for casual narration"
1484
+ },
1485
+ "prompt.style.technical": {
1486
+ value: "Usa un lenguaje técnico preciso. Sé detallado y exacto.",
1487
+ description: "Style guide for technical narration"
1488
+ },
1489
+ "script.segment.challenge": {
1490
+ value: "El desafío: {content}",
1491
+ description: "Narration segment prefix for problems",
1492
+ placeholders: [{ name: "content", type: "string" }]
1493
+ },
1494
+ "script.segment.solution": {
1495
+ value: "La solución: {content}",
1496
+ description: "Narration segment prefix for solutions",
1497
+ placeholders: [{ name: "content", type: "string" }]
1498
+ },
1499
+ "script.segment.results": {
1500
+ value: "Los resultados: {content}",
1501
+ description: "Narration segment prefix for metrics",
1502
+ placeholders: [{ name: "content", type: "string" }]
1503
+ },
1504
+ "scene.cta.default": {
1505
+ value: "Más información",
1506
+ description: "Default call-to-action text for scenes"
1507
+ },
1508
+ "scene.hook.problem": {
1509
+ value: "El problema",
1510
+ description: "Scene hook label for problem statement"
1511
+ },
1512
+ "scene.narration.problem": {
1513
+ value: "El problema: {content}",
1514
+ description: "Scene narration for problem statement",
1515
+ placeholders: [{ name: "content", type: "string" }]
1516
+ },
1517
+ "scene.hook.solution": {
1518
+ value: "La solución",
1519
+ description: "Scene hook label for solution"
1520
+ },
1521
+ "scene.narration.solution": {
1522
+ value: "La solución: {content}",
1523
+ description: "Scene narration for solution",
1524
+ placeholders: [{ name: "content", type: "string" }]
1525
+ },
1526
+ "scene.hook.results": {
1527
+ value: "Resultados",
1528
+ description: "Scene hook label for results/metrics"
1529
+ },
1530
+ "composition.apiOverview.generates": {
1531
+ value: "Genera:",
1532
+ description: "ApiOverview heading for generated outputs"
1533
+ },
1534
+ "composition.apiOverview.tagline": {
1535
+ value: "Una spec. Todas las superficies.",
1536
+ description: "ApiOverview default tagline"
1537
+ },
1538
+ "composition.apiOverview.output.rest": {
1539
+ value: "Endpoint REST",
1540
+ description: "Generated output label: REST"
1541
+ },
1542
+ "composition.apiOverview.output.graphql": {
1543
+ value: "Mutación GraphQL",
1544
+ description: "Generated output label: GraphQL"
1545
+ },
1546
+ "composition.apiOverview.output.prisma": {
1547
+ value: "Modelo Prisma",
1548
+ description: "Generated output label: Prisma"
1549
+ },
1550
+ "composition.apiOverview.output.typescript": {
1551
+ value: "SDK TypeScript",
1552
+ description: "Generated output label: TypeScript SDK"
1553
+ },
1554
+ "composition.apiOverview.output.mcp": {
1555
+ value: "Herramienta MCP",
1556
+ description: "Generated output label: MCP Tool"
1557
+ },
1558
+ "composition.apiOverview.output.openapi": {
1559
+ value: "Spec OpenAPI",
1560
+ description: "Generated output label: OpenAPI"
1561
+ },
1562
+ "composition.socialClip.cta": {
1563
+ value: "Más información",
1564
+ description: "SocialClip default CTA"
1565
+ },
1566
+ "composition.terminal.title": {
1567
+ value: "Terminal",
1568
+ description: "TerminalDemo default window title"
1569
+ }
1570
+ }
1571
+ });
1572
+
1573
+ // src/i18n/messages.ts
1574
+ import {
1575
+ createI18nFactory
1576
+ } from "@contractspec/lib.contracts-spec/translations";
1577
+ var factory = createI18nFactory({
1578
+ specKey: "video-gen.messages",
1579
+ catalogs: [enMessages, frMessages, esMessages]
1580
+ });
1581
+ var createVideoGenI18n = factory.create;
1582
+ var getDefaultI18n = factory.getDefault;
1583
+ var resetI18nRegistry = factory.resetRegistry;
1584
+
1585
+ // src/i18n/locale.ts
1586
+ import {
1587
+ DEFAULT_LOCALE,
1588
+ SUPPORTED_LOCALES,
1589
+ resolveLocale,
1590
+ isSupportedLocale
1591
+ } from "@contractspec/lib.contracts-spec/translations";
1592
+
1593
+ // src/i18n/keys.ts
1594
+ var PROMPT_KEYS = {
1595
+ "prompt.script.system": "prompt.script.system",
1596
+ "prompt.scenePlanner.system": "prompt.scenePlanner.system",
1597
+ "prompt.style.professional": "prompt.style.professional",
1598
+ "prompt.style.casual": "prompt.style.casual",
1599
+ "prompt.style.technical": "prompt.style.technical"
1600
+ };
1601
+ var SCRIPT_KEYS = {
1602
+ "script.segment.challenge": "script.segment.challenge",
1603
+ "script.segment.solution": "script.segment.solution",
1604
+ "script.segment.results": "script.segment.results"
1605
+ };
1606
+ var SCENE_KEYS = {
1607
+ "scene.cta.default": "scene.cta.default",
1608
+ "scene.hook.problem": "scene.hook.problem",
1609
+ "scene.narration.problem": "scene.narration.problem",
1610
+ "scene.hook.solution": "scene.hook.solution",
1611
+ "scene.narration.solution": "scene.narration.solution",
1612
+ "scene.hook.results": "scene.hook.results"
1613
+ };
1614
+ var COMPOSITION_KEYS = {
1615
+ "composition.apiOverview.generates": "composition.apiOverview.generates",
1616
+ "composition.apiOverview.tagline": "composition.apiOverview.tagline",
1617
+ "composition.apiOverview.output.rest": "composition.apiOverview.output.rest",
1618
+ "composition.apiOverview.output.graphql": "composition.apiOverview.output.graphql",
1619
+ "composition.apiOverview.output.prisma": "composition.apiOverview.output.prisma",
1620
+ "composition.apiOverview.output.typescript": "composition.apiOverview.output.typescript",
1621
+ "composition.apiOverview.output.mcp": "composition.apiOverview.output.mcp",
1622
+ "composition.apiOverview.output.openapi": "composition.apiOverview.output.openapi",
1623
+ "composition.socialClip.cta": "composition.socialClip.cta",
1624
+ "composition.terminal.title": "composition.terminal.title"
1625
+ };
1626
+ var I18N_KEYS = {
1627
+ ...PROMPT_KEYS,
1628
+ ...SCRIPT_KEYS,
1629
+ ...SCENE_KEYS,
1630
+ ...COMPOSITION_KEYS
1631
+ };
1123
1632
  // src/generators/scene-planner.ts
1124
1633
  class ScenePlanner {
1125
1634
  llm;
1126
1635
  model;
1127
1636
  temperature;
1128
1637
  fps;
1638
+ i18n;
1129
1639
  constructor(options) {
1130
1640
  this.llm = options?.llm;
1131
1641
  this.model = options?.model;
1132
1642
  this.temperature = options?.temperature ?? 0.3;
1133
1643
  this.fps = options?.fps ?? DEFAULT_FPS;
1644
+ this.i18n = createVideoGenI18n(options?.locale);
1134
1645
  }
1135
1646
  async plan(brief) {
1136
1647
  if (this.llm) {
@@ -1140,6 +1651,7 @@ class ScenePlanner {
1140
1651
  }
1141
1652
  planDeterministic(brief) {
1142
1653
  const { content } = brief;
1654
+ const { t } = this.i18n;
1143
1655
  const scenes = [];
1144
1656
  const fps = this.fps;
1145
1657
  scenes.push({
@@ -1148,7 +1660,7 @@ class ScenePlanner {
1148
1660
  hook: content.title,
1149
1661
  message: content.summary,
1150
1662
  points: content.solutions.slice(0, 3),
1151
- cta: content.callToAction ?? "Learn more"
1663
+ cta: content.callToAction ?? t("scene.cta.default")
1152
1664
  },
1153
1665
  durationInFrames: 3 * fps,
1154
1666
  narrationText: `${content.title}. ${content.summary}`
@@ -1157,31 +1669,35 @@ class ScenePlanner {
1157
1669
  scenes.push({
1158
1670
  compositionId: "SocialClip",
1159
1671
  props: {
1160
- hook: "The Problem",
1672
+ hook: t("scene.hook.problem"),
1161
1673
  message: content.problems[0] ?? "",
1162
1674
  points: content.problems.slice(1, 4)
1163
1675
  },
1164
1676
  durationInFrames: 4 * fps,
1165
- narrationText: `The problem: ${content.problems.join(". ")}`
1677
+ narrationText: t("scene.narration.problem", {
1678
+ content: content.problems.join(". ")
1679
+ })
1166
1680
  });
1167
1681
  }
1168
1682
  if (content.solutions.length > 0) {
1169
1683
  scenes.push({
1170
1684
  compositionId: "SocialClip",
1171
1685
  props: {
1172
- hook: "The Solution",
1686
+ hook: t("scene.hook.solution"),
1173
1687
  message: content.solutions[0] ?? "",
1174
1688
  points: content.solutions.slice(1, 4)
1175
1689
  },
1176
1690
  durationInFrames: 5 * fps,
1177
- narrationText: `The solution: ${content.solutions.join(". ")}`
1691
+ narrationText: t("scene.narration.solution", {
1692
+ content: content.solutions.join(". ")
1693
+ })
1178
1694
  });
1179
1695
  }
1180
1696
  if (content.metrics && content.metrics.length > 0) {
1181
1697
  scenes.push({
1182
1698
  compositionId: "SocialClip",
1183
1699
  props: {
1184
- hook: "Results",
1700
+ hook: t("scene.hook.results"),
1185
1701
  message: content.metrics[0] ?? "",
1186
1702
  points: content.metrics.slice(1, 3)
1187
1703
  },
@@ -1218,29 +1734,17 @@ class ScenePlanner {
1218
1734
  };
1219
1735
  }
1220
1736
  async planWithLlm(brief) {
1737
+ const { t } = this.i18n;
1221
1738
  const messages = [
1222
1739
  {
1223
1740
  role: "system",
1224
1741
  content: [
1225
1742
  {
1226
1743
  type: "text",
1227
- text: `You are a video scene planner for ContractSpec marketing/documentation videos.
1228
- Given a content brief, break it into video scenes.
1229
-
1230
- Each scene must have:
1231
- - compositionId: one of "ApiOverview", "SocialClip", "TerminalDemo"
1232
- - props: the input props for that composition (see type definitions)
1233
- - durationInFrames: duration at ${this.fps}fps
1234
- - narrationText: what the narrator says during this scene
1235
-
1236
- Return a JSON object with shape:
1237
- {
1238
- "scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
1239
- "narrationScript": string
1240
- }
1241
-
1242
- Keep the total duration around ${brief.targetDurationSeconds ?? 30} seconds.
1243
- Prioritize clarity and pacing. Each scene should communicate one idea.`
1744
+ text: t("prompt.scenePlanner.system", {
1745
+ fps: this.fps,
1746
+ targetSeconds: brief.targetDurationSeconds ?? 30
1747
+ })
1244
1748
  }
1245
1749
  ]
1246
1750
  },
@@ -1280,190 +1784,29 @@ Prioritize clarity and pacing. Each scene should communicate one idea.`
1280
1784
  }
1281
1785
  }
1282
1786
 
1283
- // src/generators/script-generator.ts
1284
- class ScriptGenerator {
1285
- llm;
1286
- model;
1287
- temperature;
1288
- constructor(options) {
1289
- this.llm = options?.llm;
1290
- this.model = options?.model;
1291
- this.temperature = options?.temperature ?? 0.5;
1292
- }
1293
- async generate(brief, config) {
1294
- const style = config?.style ?? "professional";
1295
- if (this.llm) {
1296
- return this.generateWithLlm(brief, style);
1297
- }
1298
- return this.generateDeterministic(brief, style);
1299
- }
1300
- generateDeterministic(brief, style) {
1301
- const segments = [];
1302
- const intro = this.formatForStyle(`${brief.title}. ${brief.summary}`, style);
1303
- segments.push({
1304
- sceneId: "intro",
1305
- text: intro,
1306
- estimatedDurationSeconds: this.estimateDuration(intro)
1307
- });
1308
- if (brief.problems.length > 0) {
1309
- const problemText = this.formatForStyle(`The challenge: ${brief.problems.join(". ")}`, style);
1310
- segments.push({
1311
- sceneId: "problems",
1312
- text: problemText,
1313
- estimatedDurationSeconds: this.estimateDuration(problemText)
1314
- });
1315
- }
1316
- if (brief.solutions.length > 0) {
1317
- const solutionText = this.formatForStyle(`The solution: ${brief.solutions.join(". ")}`, style);
1318
- segments.push({
1319
- sceneId: "solutions",
1320
- text: solutionText,
1321
- estimatedDurationSeconds: this.estimateDuration(solutionText)
1322
- });
1323
- }
1324
- if (brief.metrics && brief.metrics.length > 0) {
1325
- const metricsText = this.formatForStyle(`The results: ${brief.metrics.join(". ")}`, style);
1326
- segments.push({
1327
- sceneId: "metrics",
1328
- text: metricsText,
1329
- estimatedDurationSeconds: this.estimateDuration(metricsText)
1330
- });
1331
- }
1332
- if (brief.callToAction) {
1333
- const ctaText = this.formatForStyle(brief.callToAction, style);
1334
- segments.push({
1335
- sceneId: "cta",
1336
- text: ctaText,
1337
- estimatedDurationSeconds: this.estimateDuration(ctaText)
1338
- });
1339
- }
1340
- const fullText = segments.map((s) => s.text).join(" ");
1341
- const totalDuration = segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0);
1342
- return {
1343
- fullText,
1344
- segments,
1345
- estimatedDurationSeconds: totalDuration,
1346
- style
1347
- };
1348
- }
1349
- async generateWithLlm(brief, style) {
1350
- const styleGuide = {
1351
- professional: "Use a clear, authoritative, professional tone. Be concise and direct.",
1352
- casual: "Use a friendly, conversational tone. Be approachable and relatable.",
1353
- technical: "Use precise technical language. Be detailed and accurate."
1354
- };
1355
- const styleKey = style ?? "professional";
1356
- const messages = [
1357
- {
1358
- role: "system",
1359
- content: [
1360
- {
1361
- type: "text",
1362
- text: `You are a video narration script writer.
1363
- Write a narration script for a short video (30-60 seconds).
1364
- ${styleGuide[styleKey]}
1365
-
1366
- Return JSON with shape:
1367
- {
1368
- "segments": [{ "sceneId": string, "text": string }],
1369
- "fullText": string
1370
- }
1371
-
1372
- Scene IDs should be: "intro", "problems", "solutions", "metrics", "cta".
1373
- Only include segments that are relevant to the brief content.`
1374
- }
1375
- ]
1376
- },
1377
- {
1378
- role: "user",
1379
- content: [{ type: "text", text: JSON.stringify(brief) }]
1380
- }
1381
- ];
1382
- if (!this.llm) {
1383
- return this.generateDeterministic(brief, style);
1384
- }
1385
- try {
1386
- const response = await this.llm.chat(messages, {
1387
- model: this.model,
1388
- temperature: this.temperature,
1389
- responseFormat: "json"
1390
- });
1391
- const text = response.message.content.find((p) => p.type === "text");
1392
- if (!text || text.type !== "text") {
1393
- return this.generateDeterministic(brief, style);
1394
- }
1395
- const parsed = JSON.parse(text.text);
1396
- const segments = parsed.segments.map((s) => ({
1397
- sceneId: s.sceneId,
1398
- text: s.text,
1399
- estimatedDurationSeconds: this.estimateDuration(s.text)
1400
- }));
1401
- return {
1402
- fullText: parsed.fullText,
1403
- segments,
1404
- estimatedDurationSeconds: segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0),
1405
- style
1406
- };
1407
- } catch {
1408
- return this.generateDeterministic(brief, style);
1409
- }
1410
- }
1411
- formatForStyle(text, _style) {
1412
- return text;
1413
- }
1414
- estimateDuration(text) {
1415
- const wordCount = text.split(/\s+/).length;
1416
- return Math.ceil(wordCount / 150 * 60);
1417
- }
1418
- }
1419
-
1420
1787
  // src/generators/video-generator.ts
1421
1788
  import { VIDEO_FORMATS as VIDEO_FORMATS2 } from "@contractspec/lib.contracts-integrations/integrations/providers/video";
1422
1789
  class VideoGenerator {
1423
1790
  scenePlanner;
1424
- scriptGenerator;
1425
1791
  voice;
1426
- defaultVoiceId;
1792
+ transcriber;
1793
+ image;
1427
1794
  fps;
1428
1795
  constructor(options) {
1429
1796
  this.fps = options?.fps ?? DEFAULT_FPS;
1430
1797
  this.voice = options?.voice;
1431
- this.defaultVoiceId = options?.defaultVoiceId;
1798
+ this.transcriber = options?.transcriber;
1799
+ this.image = options?.image;
1432
1800
  this.scenePlanner = new ScenePlanner({
1433
1801
  llm: options?.llm,
1434
1802
  model: options?.model,
1435
1803
  temperature: options?.temperature,
1436
- fps: this.fps
1437
- });
1438
- this.scriptGenerator = new ScriptGenerator({
1439
- llm: options?.llm,
1440
- model: options?.model,
1441
- temperature: options?.temperature
1804
+ fps: this.fps,
1805
+ locale: options?.locale
1442
1806
  });
1443
1807
  }
1444
1808
  async generate(brief) {
1445
1809
  const scenePlan = await this.scenePlanner.plan(brief);
1446
- let narrationAudio;
1447
- if (brief.narration?.enabled && this.voice) {
1448
- const script = await this.scriptGenerator.generate(brief.content, brief.narration);
1449
- const voiceId = brief.narration.voiceId ?? this.defaultVoiceId;
1450
- if (voiceId && script.fullText) {
1451
- try {
1452
- const result = await this.voice.synthesize({
1453
- text: script.fullText,
1454
- voiceId,
1455
- format: "mp3"
1456
- });
1457
- narrationAudio = {
1458
- data: result.audio,
1459
- format: "mp3",
1460
- durationSeconds: result.durationSeconds ?? script.estimatedDurationSeconds,
1461
- volume: 1
1462
- };
1463
- } catch {}
1464
- }
1465
- }
1466
- const format = brief.format ?? VIDEO_FORMATS2.landscape;
1467
1810
  const scenes = scenePlan.scenes.map((planned, i) => ({
1468
1811
  id: `scene-${i}`,
1469
1812
  compositionId: planned.compositionId,
@@ -1471,6 +1814,70 @@ class VideoGenerator {
1471
1814
  durationInFrames: planned.durationInFrames,
1472
1815
  narrationText: planned.narrationText
1473
1816
  }));
1817
+ let ttsProject;
1818
+ let narrationAudio;
1819
+ if (brief.narration?.enabled && this.voice) {
1820
+ try {
1821
+ ttsProject = await this.voice.synthesizeForVideo({
1822
+ content: brief.content,
1823
+ scenePlan: {
1824
+ scenes: scenes.map((s) => ({
1825
+ id: s.id,
1826
+ compositionId: s.compositionId,
1827
+ durationInFrames: s.durationInFrames,
1828
+ narrationText: s.narrationText
1829
+ })),
1830
+ estimatedDurationSeconds: scenePlan.estimatedDurationSeconds
1831
+ },
1832
+ voice: { voiceId: brief.narration.voiceId ?? "" },
1833
+ pacing: { strategy: "scene-matched" },
1834
+ fps: this.fps
1835
+ });
1836
+ if (ttsProject.timingMap) {
1837
+ for (const seg of ttsProject.timingMap.segments) {
1838
+ const scene = scenes.find((s) => s.id === seg.sceneId);
1839
+ if (scene) {
1840
+ scene.durationInFrames = seg.recommendedSceneDurationInFrames;
1841
+ }
1842
+ }
1843
+ }
1844
+ if (ttsProject.assembledAudio) {
1845
+ narrationAudio = {
1846
+ data: ttsProject.assembledAudio.data,
1847
+ format: ttsProject.assembledAudio.format === "wav" ? "wav" : "mp3",
1848
+ durationSeconds: (ttsProject.assembledAudio.durationMs ?? 0) / 1000,
1849
+ volume: 1
1850
+ };
1851
+ }
1852
+ } catch {}
1853
+ }
1854
+ let subtitles;
1855
+ if (this.transcriber && ttsProject?.assembledAudio) {
1856
+ try {
1857
+ const transcription = await this.transcriber.transcribe({
1858
+ audio: ttsProject.assembledAudio,
1859
+ subtitleFormat: "vtt"
1860
+ });
1861
+ subtitles = transcription.subtitles;
1862
+ } catch {}
1863
+ }
1864
+ let thumbnail;
1865
+ if (this.image) {
1866
+ try {
1867
+ const thumbProject = await this.image.generate({
1868
+ content: brief.content,
1869
+ purpose: "video-thumbnail",
1870
+ format: "png",
1871
+ style: "photorealistic"
1872
+ });
1873
+ const imageUrl = thumbProject.results?.images[0]?.url;
1874
+ thumbnail = {
1875
+ prompt: thumbProject.prompt.text,
1876
+ ...imageUrl ? { imageUrl } : {}
1877
+ };
1878
+ } catch {}
1879
+ }
1880
+ const format = brief.format ?? VIDEO_FORMATS2.landscape;
1474
1881
  const totalDurationInFrames = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
1475
1882
  const project = {
1476
1883
  id: generateProjectId(),
@@ -1478,7 +1885,10 @@ class VideoGenerator {
1478
1885
  totalDurationInFrames,
1479
1886
  fps: this.fps,
1480
1887
  format,
1481
- audio: narrationAudio ? { narration: narrationAudio } : undefined
1888
+ audio: narrationAudio ? { narration: narrationAudio } : undefined,
1889
+ subtitles,
1890
+ voiceTimingMap: ttsProject?.timingMap,
1891
+ thumbnail
1482
1892
  };
1483
1893
  return project;
1484
1894
  }
@@ -1538,7 +1948,6 @@ export {
1538
1948
  TerminalDemo,
1539
1949
  Terminal,
1540
1950
  SocialClip,
1541
- ScriptGenerator,
1542
1951
  SceneTransitionWrapper,
1543
1952
  ScenePlanner,
1544
1953
  ProgressBar,