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