@meframe/core 0.5.7 → 0.5.9

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 (49) hide show
  1. package/dist/meframe-service/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  2. package/dist/{node_modules → meframe-service/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +14 -14
  3. package/dist/meframe-service/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  4. package/dist/model/CompositionModel.d.ts.map +1 -1
  5. package/dist/model/CompositionModel.js +12 -0
  6. package/dist/model/CompositionModel.js.map +1 -1
  7. package/dist/model/types.d.ts +4 -0
  8. package/dist/model/types.d.ts.map +1 -1
  9. package/dist/model/types.js.map +1 -1
  10. package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
  11. package/dist/orchestrator/CompositionPlanner.js +14 -0
  12. package/dist/orchestrator/CompositionPlanner.js.map +1 -1
  13. package/dist/stages/compose/font-system/FontManager.js +1 -1
  14. package/dist/stages/compose/font-system/FontManager.js.map +1 -1
  15. package/dist/stages/compose/font-system/font-templates.js +4 -4
  16. package/dist/stages/compose/font-system/font-templates.js.map +1 -1
  17. package/dist/stages/compose/font-system/types.d.ts +1 -1
  18. package/dist/stages/compose/font-system/types.d.ts.map +1 -1
  19. package/dist/stages/compose/instructions.d.ts +2 -2
  20. package/dist/stages/compose/instructions.d.ts.map +1 -1
  21. package/dist/stages/compose/text-renderers/basic-text-renderer.js +3 -3
  22. package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -1
  23. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.d.ts.map +1 -1
  24. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js +6 -3
  25. package/dist/stages/compose/text-renderers/caption-stagger-entrance-renderer.js.map +1 -1
  26. package/dist/stages/compose/text-renderers/character-ktv-renderer.js +3 -3
  27. package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -1
  28. package/dist/stages/compose/text-renderers/word-by-word-renderer.js +7 -7
  29. package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -1
  30. package/dist/stages/compose/text-renderers/word-fancy-renderer.js +3 -3
  31. package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -1
  32. package/dist/stages/compose/text-utils/caption-layout.d.ts +1 -1
  33. package/dist/stages/compose/text-utils/caption-layout.d.ts.map +1 -1
  34. package/dist/stages/compose/text-utils/caption-layout.js +1 -1
  35. package/dist/stages/compose/text-utils/caption-layout.js.map +1 -1
  36. package/dist/stages/compose/text-utils/text-layout-cache.js +1 -1
  37. package/dist/stages/compose/text-utils/text-layout-cache.js.map +1 -1
  38. package/dist/stages/compose/types.d.ts +2 -2
  39. package/dist/stages/compose/types.d.ts.map +1 -1
  40. package/dist/stages/mux/MP4Muxer.js +1 -1
  41. package/dist/utils/mp4box.js +1 -1
  42. package/dist/workers/stages/export/{export.worker.p7X_YtxQ.js → export.worker.CWPrx2Nf.js} +24 -21
  43. package/dist/workers/stages/export/export.worker.CWPrx2Nf.js.map +1 -0
  44. package/dist/workers/worker-manifest.json +1 -1
  45. package/package.json +1 -1
  46. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  47. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  48. package/dist/workers/stages/export/export.worker.p7X_YtxQ.js.map +0 -1
  49. /package/dist/{node_modules → meframe-service/node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"font-templates.js","sources":["../../../../src/stages/compose/font-system/font-templates.ts"],"sourcesContent":["import type { TextStyle, ContainerStyle, GlobalPositionStyle } from './types';\n\nexport interface FontTemplate {\n name: string;\n textStyle?: Partial<TextStyle>;\n containerStyle?: Partial<ContainerStyle>;\n globalPosition?: Partial<GlobalPositionStyle>;\n}\n\nexport function mergeFontTemplates(...templates: FontTemplate[]): FontTemplate {\n const merged: FontTemplate = { name: 'merged' };\n\n for (const template of templates) {\n if (template.textStyle) {\n merged.textStyle = { ...merged.textStyle, ...template.textStyle };\n }\n if (template.containerStyle) {\n merged.containerStyle = { ...merged.containerStyle, ...template.containerStyle };\n }\n if (template.globalPosition) {\n merged.globalPosition = { ...merged.globalPosition, ...template.globalPosition };\n }\n }\n\n return merged;\n}\n\nconst normal30: FontTemplate = {\n name: 'normal-30',\n textStyle: { fontSize: 30 },\n};\n\nconst normal40: FontTemplate = {\n name: 'normal-40',\n textStyle: { fontSize: 40 },\n};\n\nconst normal50: FontTemplate = {\n name: 'normal-50',\n textStyle: { fontSize: 50 },\n};\n\nconst normal60: FontTemplate = {\n name: 'normal-60',\n textStyle: { fontSize: 60 },\n};\n\nconst normal80: FontTemplate = {\n name: 'normal-80',\n textStyle: { fontSize: 80 },\n};\n\nconst bold40: FontTemplate = {\n name: 'bold-40',\n textStyle: { fontWeight: 'bold', fontSize: 40 },\n};\n\nconst bold50: FontTemplate = {\n name: 'bold-50',\n textStyle: { fontWeight: 'bold', fontSize: 50 },\n};\n\nconst bold60: FontTemplate = {\n name: 'bold-60',\n textStyle: { fontWeight: 'bold', letterSpacing: '0.05em', fontSize: 60 },\n};\n\nconst fillBlack: FontTemplate = {\n name: 'fill-black',\n textStyle: { fill: 'rgb(0, 0, 0)' },\n};\n\nconst fillWhite: FontTemplate = {\n name: 'fill-white',\n textStyle: { fill: 'rgb(255, 255, 255)' },\n};\n\nconst fillYellow: FontTemplate = {\n name: 'fill-yellow',\n textStyle: { fill: 'rgb(255, 215, 0)' },\n};\n\nconst fillRed: FontTemplate = {\n name: 'fill-red',\n textStyle: { fill: 'rgb(255, 77, 77)' },\n};\n\nconst fillPink: FontTemplate = {\n name: 'fill-pink',\n textStyle: { fill: 'rgb(255, 192, 203)' },\n};\n\nconst strokeBlack: FontTemplate = {\n name: 'stroke-black',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n stroke: 'rgb(0, 0, 0)',\n },\n};\n\nconst strokeWhite: FontTemplate = {\n name: 'stroke-white',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n stroke: 'rgb(255, 255, 255)',\n },\n};\n\nconst strokeYellow: FontTemplate = {\n name: 'stroke-yellow',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n stroke: 'rgb(255, 215, 0)',\n },\n};\n\nconst stroke0: FontTemplate = {\n name: 'stroke-0',\n textStyle: { strokeWidth: 0 },\n};\n\nconst stroke2: FontTemplate = {\n name: 'stroke-2',\n textStyle: { strokeWidth: 2 },\n};\n\nconst stroke4: FontTemplate = {\n name: 'stroke-4',\n textStyle: { strokeWidth: 4 },\n};\n\nconst stroke6: FontTemplate = {\n name: 'stroke-6',\n textStyle: { strokeWidth: 6 },\n};\n\nconst stroke8: FontTemplate = {\n name: 'stroke-8',\n textStyle: { strokeWidth: 8 },\n};\n\nconst stroke16: FontTemplate = {\n name: 'stroke-16',\n textStyle: { strokeWidth: 16 },\n};\n\nconst strokeBlack4 = mergeFontTemplates(strokeBlack, stroke4);\nstrokeBlack4.name = 'stroke-black-4';\n\nconst strokeBlack6 = mergeFontTemplates(strokeBlack, stroke6);\nstrokeBlack6.name = 'stroke-black-6';\n\nconst strokeBlack8 = mergeFontTemplates(strokeBlack, stroke8);\nstrokeBlack8.name = 'stroke-black-8';\n\nconst strokeBlack16 = mergeFontTemplates(strokeBlack, stroke16);\nstrokeBlack16.name = 'stroke-black-16';\n\nconst strokeWhite4 = mergeFontTemplates(strokeWhite, stroke4);\nstrokeWhite4.name = 'stroke-white-4';\n\nconst strokeWhite8 = mergeFontTemplates(strokeWhite, stroke8);\nstrokeWhite8.name = 'stroke-white-8';\n\nconst strokeYellow4 = mergeFontTemplates(strokeYellow, stroke4);\nstrokeYellow4.name = 'stroke-yellow-4';\n\nconst strokeYellow8 = mergeFontTemplates(strokeYellow, stroke8);\nstrokeYellow8.name = 'stroke-yellow-8';\n\nconst containerFull: FontTemplate = {\n name: 'container-full',\n containerStyle: { width: '100%' },\n};\n\nconst container60: FontTemplate = {\n name: 'container-60',\n containerStyle: { width: '60%' },\n};\n\nconst container80: FontTemplate = {\n name: 'container-80',\n containerStyle: { width: '80%' },\n};\n\nconst container95: FontTemplate = {\n name: 'container-95',\n containerStyle: { width: '95%' },\n};\n\nconst container98: FontTemplate = {\n name: 'container-98',\n containerStyle: { width: '98%' },\n};\n\nconst containerBlack: FontTemplate = {\n name: 'container-black',\n containerStyle: {\n backgroundColor: 'black',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerWhite: FontTemplate = {\n name: 'container-white',\n containerStyle: {\n backgroundColor: 'white',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerYellow: FontTemplate = {\n name: 'container-yellow',\n containerStyle: {\n backgroundColor: 'yellow',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerBlackOpacity40: FontTemplate = {\n name: 'container-black-opacity-40',\n containerStyle: {\n backgroundColor: 'rgba(0, 0, 0, 0.4)',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst globalCenter: FontTemplate = {\n name: 'global-center',\n globalPosition: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n};\n\nconst globalHorizontalCenter: FontTemplate = {\n name: 'global-horizontal-center',\n globalPosition: {\n display: 'flex',\n justifyContent: 'center',\n },\n};\n\nconst globalTop5: FontTemplate = {\n name: 'global-top-5',\n globalPosition: {\n position: 'absolute',\n top: '5%',\n },\n};\n\nconst globalTop10: FontTemplate = {\n name: 'global-top-10',\n globalPosition: {\n position: 'absolute',\n top: '10%',\n },\n};\n\nconst globalTop20: FontTemplate = {\n name: 'global-top-20',\n globalPosition: {\n position: 'absolute',\n top: '20%',\n },\n};\n\nconst globalTop70: FontTemplate = {\n name: 'global-top-70',\n globalPosition: {\n position: 'absolute',\n top: '70%',\n },\n};\n\nconst globalTop80: FontTemplate = {\n name: 'global-top-80',\n globalPosition: {\n position: 'absolute',\n top: '80%',\n },\n};\n\nconst globalTop92: FontTemplate = {\n name: 'global-top-92',\n globalPosition: {\n position: 'absolute',\n top: '92%',\n },\n};\n\nconst globalBottom5: FontTemplate = {\n name: 'global-bottom-5',\n globalPosition: {\n position: 'absolute',\n bottom: '5%',\n },\n};\n\nconst globalBottom10: FontTemplate = {\n name: 'global-bottom-10',\n globalPosition: {\n position: 'absolute',\n bottom: '10%',\n },\n};\n\nconst globalBottom15: FontTemplate = {\n name: 'global-bottom-15',\n globalPosition: {\n position: 'absolute',\n bottom: '15%',\n },\n};\n\nconst yellowText = mergeFontTemplates(normal40, fillYellow, strokeBlack8);\nyellowText.name = 'yellowText';\n\nconst whiteText = mergeFontTemplates(normal40, fillWhite, strokeBlack8);\nwhiteText.name = 'whiteText';\n\nconst blackText = mergeFontTemplates(normal40, fillBlack, strokeWhite8);\nblackText.name = 'blackText';\n\nconst baseSubtitle = mergeFontTemplates(normal40, fillWhite, strokeBlack8, globalTop70);\nbaseSubtitle.name = 'baseSubtitle';\n\nconst baseSubtitleCenter = mergeFontTemplates(\n normal40,\n fillWhite,\n strokeBlack8,\n globalTop70,\n container60\n);\nbaseSubtitleCenter.name = 'baseSubtitleCenter';\n\nconst baseSubtitle16_9 = mergeFontTemplates(normal40, fillWhite, strokeBlack4, globalTop92);\nbaseSubtitle16_9.name = 'baseSubtitle16_9';\n\nconst yellowSubtitle = mergeFontTemplates(yellowText, globalTop70);\nyellowSubtitle.name = 'yellowSubtitle';\n\nconst whiteSubtitle = mergeFontTemplates(whiteText, globalTop70);\nwhiteSubtitle.name = 'whiteSubtitle';\n\nconst blackSubtitle = mergeFontTemplates(blackText, globalTop70);\nblackSubtitle.name = 'blackSubtitle';\n\nconst subtitleBackgroundBlack = mergeFontTemplates(\n normal40,\n fillWhite,\n globalTop70,\n containerBlack\n);\nsubtitleBackgroundBlack.name = 'subtitleBackgroundBlack';\n\nconst title = mergeFontTemplates(yellowText, bold50, globalTop5, container95);\ntitle.name = 'title';\n\nconst subtitle = mergeFontTemplates(normal30, fillWhite, strokeBlack8, globalTop5, container80);\nsubtitle.name = 'subtitle';\n\nexport const FONT_TEMPLATES: Record<string, FontTemplate> = {\n 'normal-30': normal30,\n 'normal-40': normal40,\n 'normal-50': normal50,\n 'normal-60': normal60,\n 'normal-80': normal80,\n 'bold-40': bold40,\n 'bold-50': bold50,\n 'bold-60': bold60,\n 'fill-black': fillBlack,\n 'fill-white': fillWhite,\n 'fill-yellow': fillYellow,\n 'fill-red': fillRed,\n 'fill-pink': fillPink,\n 'stroke-black': strokeBlack,\n 'stroke-white': strokeWhite,\n 'stroke-yellow': strokeYellow,\n 'stroke-0': stroke0,\n 'stroke-2': stroke2,\n 'stroke-4': stroke4,\n 'stroke-6': stroke6,\n 'stroke-8': stroke8,\n 'stroke-16': stroke16,\n 'stroke-black-4': strokeBlack4,\n 'stroke-black-6': strokeBlack6,\n 'stroke-black-8': strokeBlack8,\n 'stroke-black-16': strokeBlack16,\n 'stroke-white-4': strokeWhite4,\n 'stroke-white-8': strokeWhite8,\n 'stroke-yellow-4': strokeYellow4,\n 'stroke-yellow-8': strokeYellow8,\n 'container-full': containerFull,\n 'container-60': container60,\n 'container-80': container80,\n 'container-95': container95,\n 'container-98': container98,\n 'container-black': containerBlack,\n 'container-white': containerWhite,\n 'container-yellow': containerYellow,\n 'container-black-opacity-40': containerBlackOpacity40,\n 'global-center': globalCenter,\n 'global-horizontal-center': globalHorizontalCenter,\n 'global-top-5': globalTop5,\n 'global-top-10': globalTop10,\n 'global-top-20': globalTop20,\n 'global-top-70': globalTop70,\n 'global-top-80': globalTop80,\n 'global-top-92': globalTop92,\n 'global-bottom-5': globalBottom5,\n 'global-bottom-10': globalBottom10,\n 'global-bottom-15': globalBottom15,\n yellowText,\n whiteText,\n blackText,\n baseSubtitle,\n baseSubtitleCenter,\n baseSubtitle16_9,\n yellowSubtitle,\n whiteSubtitle,\n blackSubtitle,\n subtitleBackgroundBlack,\n title,\n subtitle,\n};\n\nexport function getTemplate(templateName: string): FontTemplate {\n const templateNames = templateName.split(' ').filter(Boolean);\n const templates = templateNames\n .map((name) => FONT_TEMPLATES[name])\n .filter((t): t is FontTemplate => t !== undefined);\n\n if (templates.length === 0) {\n return {\n name: 'default',\n textStyle: {\n fontSize: 40,\n fontWeight: 400,\n fontFamily: 'Arial, sans-serif',\n fill: 'rgb(255, 255, 255)',\n stroke: 'rgb(0, 0, 0)',\n strokeWidth: 8,\n lineHeight: 1.2,\n },\n };\n }\n\n if (templates.length === 1) {\n return templates[0]!;\n }\n\n return mergeFontTemplates(...templates);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"font-templates.js","sources":["../../../../src/stages/compose/font-system/font-templates.ts"],"sourcesContent":["import type { TextStyle, ContainerStyle, GlobalPositionStyle } from './types';\n\nexport interface FontTemplate {\n name: string;\n textStyle?: Partial<TextStyle>;\n containerStyle?: Partial<ContainerStyle>;\n globalPosition?: Partial<GlobalPositionStyle>;\n}\n\nexport function mergeFontTemplates(...templates: FontTemplate[]): FontTemplate {\n const merged: FontTemplate = { name: 'merged' };\n\n for (const template of templates) {\n if (template.textStyle) {\n merged.textStyle = { ...merged.textStyle, ...template.textStyle };\n }\n if (template.containerStyle) {\n merged.containerStyle = { ...merged.containerStyle, ...template.containerStyle };\n }\n if (template.globalPosition) {\n merged.globalPosition = { ...merged.globalPosition, ...template.globalPosition };\n }\n }\n\n return merged;\n}\n\nconst normal30: FontTemplate = {\n name: 'normal-30',\n textStyle: { fontSize: 30 },\n};\n\nconst normal40: FontTemplate = {\n name: 'normal-40',\n textStyle: { fontSize: 40 },\n};\n\nconst normal50: FontTemplate = {\n name: 'normal-50',\n textStyle: { fontSize: 50 },\n};\n\nconst normal60: FontTemplate = {\n name: 'normal-60',\n textStyle: { fontSize: 60 },\n};\n\nconst normal80: FontTemplate = {\n name: 'normal-80',\n textStyle: { fontSize: 80 },\n};\n\nconst bold40: FontTemplate = {\n name: 'bold-40',\n textStyle: { fontWeight: 'bold', fontSize: 40 },\n};\n\nconst bold50: FontTemplate = {\n name: 'bold-50',\n textStyle: { fontWeight: 'bold', fontSize: 50 },\n};\n\nconst bold60: FontTemplate = {\n name: 'bold-60',\n textStyle: { fontWeight: 'bold', letterSpacing: '0.05em', fontSize: 60 },\n};\n\nconst fillBlack: FontTemplate = {\n name: 'fill-black',\n textStyle: { fill: 'rgb(0, 0, 0)' },\n};\n\nconst fillWhite: FontTemplate = {\n name: 'fill-white',\n textStyle: { fill: 'rgb(255, 255, 255)' },\n};\n\nconst fillYellow: FontTemplate = {\n name: 'fill-yellow',\n textStyle: { fill: 'rgb(255, 215, 0)' },\n};\n\nconst fillRed: FontTemplate = {\n name: 'fill-red',\n textStyle: { fill: 'rgb(255, 77, 77)' },\n};\n\nconst fillPink: FontTemplate = {\n name: 'fill-pink',\n textStyle: { fill: 'rgb(255, 192, 203)' },\n};\n\nconst strokeBlack: FontTemplate = {\n name: 'stroke-black',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n strokeColor: 'rgb(0, 0, 0)',\n },\n};\n\nconst strokeWhite: FontTemplate = {\n name: 'stroke-white',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n strokeColor: 'rgb(255, 255, 255)',\n },\n};\n\nconst strokeYellow: FontTemplate = {\n name: 'stroke-yellow',\n textStyle: {\n paintOrder: 'stroke',\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n strokeColor: 'rgb(255, 215, 0)',\n },\n};\n\nconst stroke0: FontTemplate = {\n name: 'stroke-0',\n textStyle: { strokeWidth: 0 },\n};\n\nconst stroke2: FontTemplate = {\n name: 'stroke-2',\n textStyle: { strokeWidth: 2 },\n};\n\nconst stroke4: FontTemplate = {\n name: 'stroke-4',\n textStyle: { strokeWidth: 4 },\n};\n\nconst stroke6: FontTemplate = {\n name: 'stroke-6',\n textStyle: { strokeWidth: 6 },\n};\n\nconst stroke8: FontTemplate = {\n name: 'stroke-8',\n textStyle: { strokeWidth: 8 },\n};\n\nconst stroke16: FontTemplate = {\n name: 'stroke-16',\n textStyle: { strokeWidth: 16 },\n};\n\nconst strokeBlack4 = mergeFontTemplates(strokeBlack, stroke4);\nstrokeBlack4.name = 'stroke-black-4';\n\nconst strokeBlack6 = mergeFontTemplates(strokeBlack, stroke6);\nstrokeBlack6.name = 'stroke-black-6';\n\nconst strokeBlack8 = mergeFontTemplates(strokeBlack, stroke8);\nstrokeBlack8.name = 'stroke-black-8';\n\nconst strokeBlack16 = mergeFontTemplates(strokeBlack, stroke16);\nstrokeBlack16.name = 'stroke-black-16';\n\nconst strokeWhite4 = mergeFontTemplates(strokeWhite, stroke4);\nstrokeWhite4.name = 'stroke-white-4';\n\nconst strokeWhite8 = mergeFontTemplates(strokeWhite, stroke8);\nstrokeWhite8.name = 'stroke-white-8';\n\nconst strokeYellow4 = mergeFontTemplates(strokeYellow, stroke4);\nstrokeYellow4.name = 'stroke-yellow-4';\n\nconst strokeYellow8 = mergeFontTemplates(strokeYellow, stroke8);\nstrokeYellow8.name = 'stroke-yellow-8';\n\nconst containerFull: FontTemplate = {\n name: 'container-full',\n containerStyle: { width: '100%' },\n};\n\nconst container60: FontTemplate = {\n name: 'container-60',\n containerStyle: { width: '60%' },\n};\n\nconst container80: FontTemplate = {\n name: 'container-80',\n containerStyle: { width: '80%' },\n};\n\nconst container95: FontTemplate = {\n name: 'container-95',\n containerStyle: { width: '95%' },\n};\n\nconst container98: FontTemplate = {\n name: 'container-98',\n containerStyle: { width: '98%' },\n};\n\nconst containerBlack: FontTemplate = {\n name: 'container-black',\n containerStyle: {\n backgroundColor: 'black',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerWhite: FontTemplate = {\n name: 'container-white',\n containerStyle: {\n backgroundColor: 'white',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerYellow: FontTemplate = {\n name: 'container-yellow',\n containerStyle: {\n backgroundColor: 'yellow',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst containerBlackOpacity40: FontTemplate = {\n name: 'container-black-opacity-40',\n containerStyle: {\n backgroundColor: 'rgba(0, 0, 0, 0.4)',\n padding: '10px 10px 4px 10px',\n borderRadius: 10,\n width: '80%',\n },\n};\n\nconst globalCenter: FontTemplate = {\n name: 'global-center',\n globalPosition: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n};\n\nconst globalHorizontalCenter: FontTemplate = {\n name: 'global-horizontal-center',\n globalPosition: {\n display: 'flex',\n justifyContent: 'center',\n },\n};\n\nconst globalTop5: FontTemplate = {\n name: 'global-top-5',\n globalPosition: {\n position: 'absolute',\n top: '5%',\n },\n};\n\nconst globalTop10: FontTemplate = {\n name: 'global-top-10',\n globalPosition: {\n position: 'absolute',\n top: '10%',\n },\n};\n\nconst globalTop20: FontTemplate = {\n name: 'global-top-20',\n globalPosition: {\n position: 'absolute',\n top: '20%',\n },\n};\n\nconst globalTop70: FontTemplate = {\n name: 'global-top-70',\n globalPosition: {\n position: 'absolute',\n top: '70%',\n },\n};\n\nconst globalTop80: FontTemplate = {\n name: 'global-top-80',\n globalPosition: {\n position: 'absolute',\n top: '80%',\n },\n};\n\nconst globalTop92: FontTemplate = {\n name: 'global-top-92',\n globalPosition: {\n position: 'absolute',\n top: '92%',\n },\n};\n\nconst globalBottom5: FontTemplate = {\n name: 'global-bottom-5',\n globalPosition: {\n position: 'absolute',\n bottom: '5%',\n },\n};\n\nconst globalBottom10: FontTemplate = {\n name: 'global-bottom-10',\n globalPosition: {\n position: 'absolute',\n bottom: '10%',\n },\n};\n\nconst globalBottom15: FontTemplate = {\n name: 'global-bottom-15',\n globalPosition: {\n position: 'absolute',\n bottom: '15%',\n },\n};\n\nconst yellowText = mergeFontTemplates(normal40, fillYellow, strokeBlack8);\nyellowText.name = 'yellowText';\n\nconst whiteText = mergeFontTemplates(normal40, fillWhite, strokeBlack8);\nwhiteText.name = 'whiteText';\n\nconst blackText = mergeFontTemplates(normal40, fillBlack, strokeWhite8);\nblackText.name = 'blackText';\n\nconst baseSubtitle = mergeFontTemplates(normal40, fillWhite, strokeBlack8, globalTop70);\nbaseSubtitle.name = 'baseSubtitle';\n\nconst baseSubtitleCenter = mergeFontTemplates(\n normal40,\n fillWhite,\n strokeBlack8,\n globalTop70,\n container60\n);\nbaseSubtitleCenter.name = 'baseSubtitleCenter';\n\nconst baseSubtitle16_9 = mergeFontTemplates(normal40, fillWhite, strokeBlack4, globalTop92);\nbaseSubtitle16_9.name = 'baseSubtitle16_9';\n\nconst yellowSubtitle = mergeFontTemplates(yellowText, globalTop70);\nyellowSubtitle.name = 'yellowSubtitle';\n\nconst whiteSubtitle = mergeFontTemplates(whiteText, globalTop70);\nwhiteSubtitle.name = 'whiteSubtitle';\n\nconst blackSubtitle = mergeFontTemplates(blackText, globalTop70);\nblackSubtitle.name = 'blackSubtitle';\n\nconst subtitleBackgroundBlack = mergeFontTemplates(\n normal40,\n fillWhite,\n globalTop70,\n containerBlack\n);\nsubtitleBackgroundBlack.name = 'subtitleBackgroundBlack';\n\nconst title = mergeFontTemplates(yellowText, bold50, globalTop5, container95);\ntitle.name = 'title';\n\nconst subtitle = mergeFontTemplates(normal30, fillWhite, strokeBlack8, globalTop5, container80);\nsubtitle.name = 'subtitle';\n\nexport const FONT_TEMPLATES: Record<string, FontTemplate> = {\n 'normal-30': normal30,\n 'normal-40': normal40,\n 'normal-50': normal50,\n 'normal-60': normal60,\n 'normal-80': normal80,\n 'bold-40': bold40,\n 'bold-50': bold50,\n 'bold-60': bold60,\n 'fill-black': fillBlack,\n 'fill-white': fillWhite,\n 'fill-yellow': fillYellow,\n 'fill-red': fillRed,\n 'fill-pink': fillPink,\n 'stroke-black': strokeBlack,\n 'stroke-white': strokeWhite,\n 'stroke-yellow': strokeYellow,\n 'stroke-0': stroke0,\n 'stroke-2': stroke2,\n 'stroke-4': stroke4,\n 'stroke-6': stroke6,\n 'stroke-8': stroke8,\n 'stroke-16': stroke16,\n 'stroke-black-4': strokeBlack4,\n 'stroke-black-6': strokeBlack6,\n 'stroke-black-8': strokeBlack8,\n 'stroke-black-16': strokeBlack16,\n 'stroke-white-4': strokeWhite4,\n 'stroke-white-8': strokeWhite8,\n 'stroke-yellow-4': strokeYellow4,\n 'stroke-yellow-8': strokeYellow8,\n 'container-full': containerFull,\n 'container-60': container60,\n 'container-80': container80,\n 'container-95': container95,\n 'container-98': container98,\n 'container-black': containerBlack,\n 'container-white': containerWhite,\n 'container-yellow': containerYellow,\n 'container-black-opacity-40': containerBlackOpacity40,\n 'global-center': globalCenter,\n 'global-horizontal-center': globalHorizontalCenter,\n 'global-top-5': globalTop5,\n 'global-top-10': globalTop10,\n 'global-top-20': globalTop20,\n 'global-top-70': globalTop70,\n 'global-top-80': globalTop80,\n 'global-top-92': globalTop92,\n 'global-bottom-5': globalBottom5,\n 'global-bottom-10': globalBottom10,\n 'global-bottom-15': globalBottom15,\n yellowText,\n whiteText,\n blackText,\n baseSubtitle,\n baseSubtitleCenter,\n baseSubtitle16_9,\n yellowSubtitle,\n whiteSubtitle,\n blackSubtitle,\n subtitleBackgroundBlack,\n title,\n subtitle,\n};\n\nexport function getTemplate(templateName: string): FontTemplate {\n const templateNames = templateName.split(' ').filter(Boolean);\n const templates = templateNames\n .map((name) => FONT_TEMPLATES[name])\n .filter((t): t is FontTemplate => t !== undefined);\n\n if (templates.length === 0) {\n return {\n name: 'default',\n textStyle: {\n fontSize: 40,\n fontWeight: 400,\n fontFamily: 'Arial, sans-serif',\n fill: 'rgb(255, 255, 255)',\n strokeColor: 'rgb(0, 0, 0)',\n strokeWidth: 8,\n lineHeight: 1.2,\n },\n };\n }\n\n if (templates.length === 1) {\n return templates[0]!;\n }\n\n return mergeFontTemplates(...templates);\n}\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ export interface TextStyle {
4
4
  fontWeight: string | number;
5
5
  fontFamily: string;
6
6
  fill: string;
7
- stroke?: string;
7
+ strokeColor?: string;
8
8
  strokeWidth?: number;
9
9
  letterSpacing?: string | number;
10
10
  lineHeight?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/font-system/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EACA,MAAM,GACN,MAAM,GACN,YAAY,GACZ,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,QAAQ,GACR,YAAY,GACZ,cAAc,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/font-system/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EACA,MAAM,GACN,MAAM,GACN,YAAY,GACZ,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,QAAQ,GACR,YAAY,GACZ,cAAc,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -74,7 +74,7 @@ export interface SerializedTextLayerPayload extends LayerPayloadBase {
74
74
  fontWeight: string | number;
75
75
  fontFamily: string;
76
76
  fill: string;
77
- stroke?: string;
77
+ strokeColor?: string;
78
78
  strokeWidth?: number;
79
79
  letterSpacing?: string | number;
80
80
  lineHeight?: number;
@@ -107,7 +107,7 @@ export interface SerializedTextLayerPayload extends LayerPayloadBase {
107
107
  highlightColor?: string;
108
108
  highlightTextStyle?: {
109
109
  fill?: string;
110
- stroke?: string;
110
+ strokeColor?: string;
111
111
  };
112
112
  [key: string]: unknown;
113
113
  };
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/instructions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/instructions.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,7 @@ function drawBasicTextLines(ctx, layer, canvasWidth, canvasHeight, lines) {
25
25
  const fontFamily = fontConfig.fontFamily;
26
26
  const fontWeight = fontConfig.fontWeight;
27
27
  const fill = fontConfig.fill;
28
- const stroke = fontConfig.stroke;
28
+ const strokeColor = fontConfig.strokeColor;
29
29
  const strokeWidth = fontConfig.strokeWidth || 0;
30
30
  const lineHeight = fontConfig.lineHeight || 1.2;
31
31
  ctx.save();
@@ -39,8 +39,8 @@ function drawBasicTextLines(ctx, layer, canvasWidth, canvasHeight, lines) {
39
39
  for (let i = 0; i < lines.length; i++) {
40
40
  const line = lines[i];
41
41
  const y = startY + i * fontSize * lineHeight + fontSize / 2;
42
- if (stroke && strokeWidth > 0) {
43
- ctx.strokeStyle = stroke;
42
+ if (strokeColor && strokeWidth > 0) {
43
+ ctx.strokeStyle = strokeColor;
44
44
  ctx.lineWidth = strokeWidth;
45
45
  ctx.strokeText(line, canvasWidth / 2, y);
46
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"basic-text-renderer.js","sources":["../../../../src/stages/compose/text-renderers/basic-text-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport {\n getCachedEvenLinesWithWords,\n getCachedWrapText,\n getCachedBasicTextRaster,\n} from '../text-utils/text-layout-cache';\nimport { getLetterCaseText } from '../text-utils/text-metrics';\nimport { springEasing, interpolate } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nfunction drawBasicTextLines(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n lines: string[]\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const stroke = fontConfig.stroke;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const y = startY + i * fontSize * lineHeight + fontSize / 2;\n\n if (stroke && strokeWidth > 0) {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(line, canvasWidth / 2, y);\n }\n ctx.fillStyle = fill;\n ctx.fillText(line, canvasWidth / 2, y);\n }\n\n ctx.restore();\n}\n\nfunction resolveBasicTextLines(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number\n): string[] {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return [];\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = needsSpace ? text.split(/\\s+/) : Array.from(text);\n return getCachedEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontConfig.fontSize,\n needsSpace,\n fontConfig.fontFamily,\n fontConfig.fontWeight\n );\n }\n\n return getCachedWrapText(\n ctx,\n text,\n maxWidth,\n fontConfig.fontSize,\n fontConfig.fontFamily,\n fontConfig.fontWeight\n );\n}\n\nexport function renderBasicText(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n _relativeFrame: number\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const raster = getCachedBasicTextRaster(layer, canvasWidth, canvasHeight, (offCtx) => {\n const lines = resolveBasicTextLines(offCtx, layer, canvasWidth);\n if (lines.length === 0) return;\n drawBasicTextLines(offCtx, layer, canvasWidth, canvasHeight, lines);\n });\n\n if (raster) {\n ctx.drawImage(raster, 0, 0);\n return;\n }\n\n const lines = resolveBasicTextLines(ctx, layer, canvasWidth);\n if (lines.length === 0) return;\n drawBasicTextLines(ctx, layer, canvasWidth, canvasHeight, lines);\n}\n\nexport function renderTextWithEntrance(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const entrance = springEasing(relativeFrame, {\n damping: 200,\n mass: 1,\n stiffness: 300,\n });\n\n const opacity = interpolate(entrance, [0, 1], [0, 1]);\n const scale = interpolate(entrance, [0, 1], [0.9, 1]);\n\n ctx.save();\n ctx.globalAlpha = opacity;\n ctx.translate(canvasWidth / 2, canvasHeight / 2);\n ctx.scale(scale, scale);\n ctx.translate(-canvasWidth / 2, -canvasHeight / 2);\n\n renderBasicText(ctx, layer, canvasWidth, canvasHeight, relativeFrame);\n\n ctx.restore();\n}\n"],"names":["lines"],"mappings":"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"}
1
+ {"version":3,"file":"basic-text-renderer.js","sources":["../../../../src/stages/compose/text-renderers/basic-text-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport {\n getCachedEvenLinesWithWords,\n getCachedWrapText,\n getCachedBasicTextRaster,\n} from '../text-utils/text-layout-cache';\nimport { getLetterCaseText } from '../text-utils/text-metrics';\nimport { springEasing, interpolate } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nfunction drawBasicTextLines(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n lines: string[]\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const strokeColor = fontConfig.strokeColor;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const y = startY + i * fontSize * lineHeight + fontSize / 2;\n\n if (strokeColor && strokeWidth > 0) {\n ctx.strokeStyle = strokeColor;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(line, canvasWidth / 2, y);\n }\n ctx.fillStyle = fill;\n ctx.fillText(line, canvasWidth / 2, y);\n }\n\n ctx.restore();\n}\n\nfunction resolveBasicTextLines(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number\n): string[] {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return [];\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = needsSpace ? text.split(/\\s+/) : Array.from(text);\n return getCachedEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontConfig.fontSize,\n needsSpace,\n fontConfig.fontFamily,\n fontConfig.fontWeight\n );\n }\n\n return getCachedWrapText(\n ctx,\n text,\n maxWidth,\n fontConfig.fontSize,\n fontConfig.fontFamily,\n fontConfig.fontWeight\n );\n}\n\nexport function renderBasicText(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n _relativeFrame: number\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const raster = getCachedBasicTextRaster(layer, canvasWidth, canvasHeight, (offCtx) => {\n const lines = resolveBasicTextLines(offCtx, layer, canvasWidth);\n if (lines.length === 0) return;\n drawBasicTextLines(offCtx, layer, canvasWidth, canvasHeight, lines);\n });\n\n if (raster) {\n ctx.drawImage(raster, 0, 0);\n return;\n }\n\n const lines = resolveBasicTextLines(ctx, layer, canvasWidth);\n if (lines.length === 0) return;\n drawBasicTextLines(ctx, layer, canvasWidth, canvasHeight, lines);\n}\n\nexport function renderTextWithEntrance(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const entrance = springEasing(relativeFrame, {\n damping: 200,\n mass: 1,\n stiffness: 300,\n });\n\n const opacity = interpolate(entrance, [0, 1], [0, 1]);\n const scale = interpolate(entrance, [0, 1], [0.9, 1]);\n\n ctx.save();\n ctx.globalAlpha = opacity;\n ctx.translate(canvasWidth / 2, canvasHeight / 2);\n ctx.scale(scale, scale);\n ctx.translate(-canvasWidth / 2, -canvasHeight / 2);\n\n renderBasicText(ctx, layer, canvasWidth, canvasHeight, relativeFrame);\n\n ctx.restore();\n}\n"],"names":["lines"],"mappings":";;;AAUA,SAAS,mBACP,cACA,aACA,gBAUQ;AACR,MAAI,CAAC,gBAAgB;AACnB,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,MAAI,eAAe,KAAK;AACtB,UAAM,aAAa,WAAW,eAAe,GAAG,IAAI;AACpD,WAAO,eAAe;AAAA,EACxB;AAEA,MAAI,eAAe,QAAQ;AACzB,UAAM,gBAAgB,WAAW,eAAe,MAAM,IAAI;AAC1D,WAAO,gBAAgB,IAAI,iBAAiB;AAAA,EAC9C;AAEA,MAAI,eAAe,mBAAmB,YAAY,eAAe,eAAe,UAAU;AACxF,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,SAAO,eAAe,IAAI,cAAc;AAC1C;AAEA,SAAS,mBACP,KACA,OACA,aACA,cACA,OACM;AACN,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,WAAY;AAEjB,QAAM,WAAW,WAAW;AAC5B,QAAM,aAAa,WAAW;AAC9B,QAAM,aAAa,WAAW;AAC9B,QAAM,OAAO,WAAW;AACxB,QAAM,cAAc,WAAW;AAC/B,QAAM,cAAc,WAAW,eAAe;AAC9C,QAAM,aAAa,WAAW,cAAc;AAE5C,MAAI,KAAA;AACJ,MAAI,OAAO,GAAG,UAAU,IAAI,QAAQ,MAAM,UAAU;AACpD,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,QAAM,cAAc,MAAM,SAAS,WAAW;AAC9C,QAAM,SAAS,mBAAmB,cAAc,aAAa,MAAM,YAAY,cAAc;AAE7F,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,IAAI,SAAS,IAAI,WAAW,aAAa,WAAW;AAE1D,QAAI,eAAe,cAAc,GAAG;AAClC,UAAI,cAAc;AAClB,UAAI,YAAY;AAChB,UAAI,WAAW,MAAM,cAAc,GAAG,CAAC;AAAA,IACzC;AACA,QAAI,YAAY;AAChB,QAAI,SAAS,MAAM,cAAc,GAAG,CAAC;AAAA,EACvC;AAEA,MAAI,QAAA;AACN;AAEA,SAAS,sBACP,KACA,OACA,aACU;AACV,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,WAAY,QAAO,CAAA;AAExB,QAAM,WAAW,cAAc;AAC/B,QAAM,OAAO,kBAAkB,MAAM,MAAM,MAAM,UAAU;AAE3D,MAAI,MAAM,eAAe,MAAM,YAAY,SAAS,GAAG;AACrD,UAAM,aAAa,uBAAuB,MAAM,cAAc,SAAS,IAAI;AAC3E,UAAM,QAAQ,aAAa,KAAK,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI;AAC9D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,WAAW;AAAA,MACX,WAAW;AAAA,IAAA;AAAA,EAEf;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EAAA;AAEf;AAEO,SAAS,gBACd,KACA,OACA,aACA,cACA,gBACM;AACN,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,WAAY;AAEjB,QAAM,SAAS,yBAAyB,OAAO,aAAa,cAAc,CAAC,WAAW;AACpF,UAAMA,SAAQ,sBAAsB,QAAQ,OAAO,WAAW;AAC9D,QAAIA,OAAM,WAAW,EAAG;AACxB,uBAAmB,QAAQ,OAAO,aAAa,cAAcA,MAAK;AAAA,EACpE,CAAC;AAED,MAAI,QAAQ;AACV,QAAI,UAAU,QAAQ,GAAG,CAAC;AAC1B;AAAA,EACF;AAEA,QAAM,QAAQ,sBAAsB,KAAK,OAAO,WAAW;AAC3D,MAAI,MAAM,WAAW,EAAG;AACxB,qBAAmB,KAAK,OAAO,aAAa,cAAc,KAAK;AACjE;"}
@@ -1 +1 @@
1
- {"version":3,"file":"caption-stagger-entrance-renderer.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/text-renderers/caption-stagger-entrance-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAkD1C,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,CAMR;AAED,MAAM,MAAM,oBAAoB,GAC5B,MAAM,GACN,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,QAAQ,GACR,YAAY,GACZ,cAAc,CAAC;AA2CnB,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAO5F;AAwMD,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,iCAAiC,GAAG,wBAAwB,EACjE,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oBAAoB,GAC3B,IAAI,CA0BN"}
1
+ {"version":3,"file":"caption-stagger-entrance-renderer.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/text-renderers/caption-stagger-entrance-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAkD1C,wBAAgB,YAAY,CAC1B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,CAMR;AAED,MAAM,MAAM,oBAAoB,GAC5B,MAAM,GACN,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,QAAQ,GACR,YAAY,GACZ,cAAc,CAAC;AA8CnB,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAO5F;AAwMD,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,iCAAiC,GAAG,wBAAwB,EACjE,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oBAAoB,GAC3B,IAAI,CA0BN"}
@@ -49,6 +49,9 @@ function layoutCacheKey(layer, canvasWidth, canvasHeight) {
49
49
  ts?.fontSize,
50
50
  ts?.fontFamily,
51
51
  ts?.fontWeight,
52
+ ts?.fill,
53
+ ts?.strokeColor,
54
+ ts?.strokeWidth,
52
55
  ts?.lineHeight,
53
56
  JSON.stringify(layer.fontConfig?.globalPosition ?? null)
54
57
  ].join("");
@@ -133,7 +136,7 @@ function drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset)
133
136
  const fontFamily = fontConfig.fontFamily;
134
137
  const fontWeight = fontConfig.fontWeight;
135
138
  const fill = fontConfig.fill;
136
- const stroke = fontConfig.stroke;
139
+ const strokeColor = fontConfig.strokeColor;
137
140
  const strokeWidth = fontConfig.strokeWidth || 0;
138
141
  const scalePx = fontSize / FONT_REF_PX;
139
142
  const staggerMs = DEFAULT_STAGGER_MS;
@@ -208,8 +211,8 @@ function drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset)
208
211
  } else {
209
212
  ctx.scale(sc, sc);
210
213
  }
211
- if (stroke && strokeWidth > 0) {
212
- ctx.strokeStyle = stroke;
214
+ if (strokeColor && strokeWidth > 0) {
215
+ ctx.strokeStyle = strokeColor;
213
216
  ctx.lineWidth = strokeWidth;
214
217
  ctx.strokeText(slot.ch, 0, 0);
215
218
  }
@@ -1 +1 @@
1
- {"version":3,"file":"caption-stagger-entrance-renderer.js","sources":["../../../../src/stages/compose/text-renderers/caption-stagger-entrance-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { getCachedEvenLinesWithWords, getCachedWrapText } from '../text-utils/text-layout-cache';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\n/** Matches medeo-web caption preview (`caption-anime-effects` + anime.js defaults). */\nconst DEFAULT_DURATION_MS = 800;\nconst DEFAULT_STAGGER_MS = 50;\nconst SLIDE_BASE_PX = 50;\nconst LETTER_SPREAD_BASE_PX = 20;\nconst BLUR_START_PX = 10;\nconst FONT_REF_PX = 40;\n\nfunction easeOutExpo(t: number): number {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function charProgress(\n relativeFrame: number,\n fps: number,\n charIndex: number,\n staggerMs: number,\n durationMs: number\n): number {\n const tMs = (relativeFrame / fps) * 1000;\n const startMs = charIndex * staggerMs;\n if (tMs <= startMs) return 0;\n const raw = (tMs - startMs) / durationMs;\n return easeOutExpo(Math.min(1, raw));\n}\n\nexport type CaptionStaggerPreset =\n | 'fade'\n | 'slideUp'\n | 'scale'\n | 'rotateScale'\n | 'blur'\n | 'flip3d'\n | 'typewriter'\n | 'letterSpread';\n\ninterface CharSlot {\n ch: string;\n x: number;\n y: number;\n globalIndex: number;\n}\n\ninterface CharSlotsLayout {\n slots: CharSlot[];\n fontSize: number;\n lineHeight: number;\n}\n\nconst charSlotsCache = new Map<string, CharSlotsLayout>();\nconst staggerFinalRasterCache = new Map<string, ImageBitmap>();\n\nfunction layoutCacheKey(layer: TextLayer, canvasWidth: number, canvasHeight: number): string {\n const ts = layer.fontConfig?.textStyle;\n return [\n layer.id,\n layer.text,\n layer.letterCase ?? '',\n canvasWidth,\n canvasHeight,\n ts?.fontSize,\n ts?.fontFamily,\n ts?.fontWeight,\n ts?.lineHeight,\n JSON.stringify(layer.fontConfig?.globalPosition ?? null),\n ].join('\\x1f');\n}\n\nfunction staggerRasterKey(\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n preset: CaptionStaggerPreset\n): string {\n return `${layoutCacheKey(layer, canvasWidth, canvasHeight)}\\x1f${preset}`;\n}\n\nexport function staggerEntranceEndMs(slotCount: number, preset: CaptionStaggerPreset): number {\n if (slotCount <= 0) return 0;\n const lastIndex = slotCount - 1;\n if (preset === 'typewriter') {\n return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_STAGGER_MS;\n }\n return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_DURATION_MS;\n}\n\nfunction buildCharSlots(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number\n): CharSlotsLayout | null {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return null;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const lineHeight = fontConfig.lineHeight || 1.2;\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n\n let lines: string[];\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = needsSpace ? text.split(/\\s+/) : Array.from(text);\n lines = getCachedEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n } else {\n lines = getCachedWrapText(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);\n }\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n\n const slots: CharSlot[] = [];\n let globalIndex = 0;\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let cx = canvasWidth / 2 - lineWidth / 2;\n\n for (const ch of Array.from(line)) {\n const cw = measureTextWidth(ctx, ch, fontSize, fontFamily, fontWeight);\n slots.push({\n ch,\n x: cx + cw / 2,\n y,\n globalIndex,\n });\n globalIndex++;\n cx += cw;\n }\n }\n\n ctx.restore();\n return { slots, fontSize, lineHeight };\n}\n\nfunction getCachedCharSlots(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number\n): CharSlotsLayout | null {\n const key = layoutCacheKey(layer, canvasWidth, canvasHeight);\n const cached = charSlotsCache.get(key);\n if (cached) {\n return cached;\n }\n const built = buildCharSlots(ctx, layer, canvasWidth, canvasHeight);\n if (built) {\n charSlotsCache.set(key, built);\n }\n return built;\n}\n\nfunction drawStaggerEntranceFrame(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n built: CharSlotsLayout,\n relativeFrame: number,\n fps: number,\n preset: CaptionStaggerPreset\n): void {\n const { slots, fontSize } = built;\n const fontConfig = layer.fontConfig!.textStyle!;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const stroke = fontConfig.stroke;\n const strokeWidth = fontConfig.strokeWidth || 0;\n\n const scalePx = fontSize / FONT_REF_PX;\n const staggerMs = DEFAULT_STAGGER_MS;\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const tMsGlobal = (relativeFrame / fps) * 1000;\n\n for (const slot of slots) {\n let p: number;\n if (preset === 'typewriter') {\n const startMs = slot.globalIndex * staggerMs;\n p = tMsGlobal >= startMs ? 1 : 0;\n } else {\n p = charProgress(relativeFrame, fps, slot.globalIndex, staggerMs, DEFAULT_DURATION_MS);\n }\n if (p <= 0 && preset !== 'blur') continue;\n\n const slidePx = SLIDE_BASE_PX * scalePx;\n const spreadPx = LETTER_SPREAD_BASE_PX * scalePx;\n\n ctx.save();\n\n let opacity = p;\n let tx = 0;\n let ty = 0;\n let rot = 0;\n let sc = 1;\n let sy = 1;\n let blurPx = 0;\n\n switch (preset) {\n case 'fade':\n opacity = p;\n break;\n case 'slideUp':\n opacity = p;\n ty = (1 - p) * slidePx;\n break;\n case 'scale':\n opacity = p;\n sc = Math.max(0.04, p);\n break;\n case 'rotateScale':\n opacity = p;\n rot = ((1 - p) * 45 * Math.PI) / 180;\n sc = 0.5 + p * 0.5;\n break;\n case 'blur':\n opacity = p;\n blurPx = (1 - p) * BLUR_START_PX;\n break;\n case 'flip3d':\n opacity = p;\n sy = Math.max(0.04, p);\n sc = 1;\n break;\n case 'typewriter':\n opacity = p >= 1 ? 1 : p;\n break;\n case 'letterSpread':\n opacity = p;\n tx = (1 - p) * spreadPx * slot.globalIndex;\n break;\n default:\n break;\n }\n\n ctx.globalAlpha = opacity;\n if (blurPx > 0.01) {\n ctx.filter = `blur(${blurPx}px)`;\n }\n\n ctx.translate(slot.x + tx, slot.y + ty);\n ctx.rotate(rot);\n if (preset === 'flip3d') {\n ctx.scale(1, sy);\n } else {\n ctx.scale(sc, sc);\n }\n\n if (stroke && strokeWidth > 0) {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(slot.ch, 0, 0);\n }\n ctx.fillStyle = fill;\n ctx.fillText(slot.ch, 0, 0);\n\n ctx.restore();\n }\n\n ctx.restore();\n}\n\nexport function clearCaptionStaggerCache(): void {\n charSlotsCache.clear();\n for (const bitmap of staggerFinalRasterCache.values()) {\n bitmap.close();\n }\n staggerFinalRasterCache.clear();\n}\n\n/**\n * Per-character stagger entrance aligned with medeo-web preview (anime.js easeOutExpo, 800ms, 50ms stagger).\n */\nexport function renderCaptionStaggerEntrance(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number,\n preset: CaptionStaggerPreset\n): void {\n const built = getCachedCharSlots(ctx, layer, canvasWidth, canvasHeight);\n if (!built) return;\n\n const endMs = staggerEntranceEndMs(built.slots.length, preset);\n const endFrame = Math.ceil((endMs / 1000) * fps);\n\n if (relativeFrame >= endFrame) {\n const rasterKey = staggerRasterKey(layer, canvasWidth, canvasHeight, preset);\n let bitmap = staggerFinalRasterCache.get(rasterKey);\n if (!bitmap) {\n const offscreen = new OffscreenCanvas(canvasWidth, canvasHeight);\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n drawStaggerEntranceFrame(ctx, layer, built, endFrame, fps, preset);\n return;\n }\n drawStaggerEntranceFrame(offCtx, layer, built, endFrame, fps, preset);\n bitmap = offscreen.transferToImageBitmap();\n staggerFinalRasterCache.set(rasterKey, bitmap);\n }\n ctx.drawImage(bitmap, 0, 0);\n return;\n }\n\n drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"caption-stagger-entrance-renderer.js","sources":["../../../../src/stages/compose/text-renderers/caption-stagger-entrance-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { getCachedEvenLinesWithWords, getCachedWrapText } from '../text-utils/text-layout-cache';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\n/** Matches medeo-web caption preview (`caption-anime-effects` + anime.js defaults). */\nconst DEFAULT_DURATION_MS = 800;\nconst DEFAULT_STAGGER_MS = 50;\nconst SLIDE_BASE_PX = 50;\nconst LETTER_SPREAD_BASE_PX = 20;\nconst BLUR_START_PX = 10;\nconst FONT_REF_PX = 40;\n\nfunction easeOutExpo(t: number): number {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function charProgress(\n relativeFrame: number,\n fps: number,\n charIndex: number,\n staggerMs: number,\n durationMs: number\n): number {\n const tMs = (relativeFrame / fps) * 1000;\n const startMs = charIndex * staggerMs;\n if (tMs <= startMs) return 0;\n const raw = (tMs - startMs) / durationMs;\n return easeOutExpo(Math.min(1, raw));\n}\n\nexport type CaptionStaggerPreset =\n | 'fade'\n | 'slideUp'\n | 'scale'\n | 'rotateScale'\n | 'blur'\n | 'flip3d'\n | 'typewriter'\n | 'letterSpread';\n\ninterface CharSlot {\n ch: string;\n x: number;\n y: number;\n globalIndex: number;\n}\n\ninterface CharSlotsLayout {\n slots: CharSlot[];\n fontSize: number;\n lineHeight: number;\n}\n\nconst charSlotsCache = new Map<string, CharSlotsLayout>();\nconst staggerFinalRasterCache = new Map<string, ImageBitmap>();\n\nfunction layoutCacheKey(layer: TextLayer, canvasWidth: number, canvasHeight: number): string {\n const ts = layer.fontConfig?.textStyle;\n return [\n layer.id,\n layer.text,\n layer.letterCase ?? '',\n canvasWidth,\n canvasHeight,\n ts?.fontSize,\n ts?.fontFamily,\n ts?.fontWeight,\n ts?.fill,\n ts?.strokeColor,\n ts?.strokeWidth,\n ts?.lineHeight,\n JSON.stringify(layer.fontConfig?.globalPosition ?? null),\n ].join('\\x1f');\n}\n\nfunction staggerRasterKey(\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n preset: CaptionStaggerPreset\n): string {\n return `${layoutCacheKey(layer, canvasWidth, canvasHeight)}\\x1f${preset}`;\n}\n\nexport function staggerEntranceEndMs(slotCount: number, preset: CaptionStaggerPreset): number {\n if (slotCount <= 0) return 0;\n const lastIndex = slotCount - 1;\n if (preset === 'typewriter') {\n return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_STAGGER_MS;\n }\n return lastIndex * DEFAULT_STAGGER_MS + DEFAULT_DURATION_MS;\n}\n\nfunction buildCharSlots(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number\n): CharSlotsLayout | null {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return null;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const lineHeight = fontConfig.lineHeight || 1.2;\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n\n let lines: string[];\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = needsSpace ? text.split(/\\s+/) : Array.from(text);\n lines = getCachedEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n } else {\n lines = getCachedWrapText(ctx, text, maxWidth, fontSize, fontFamily, fontWeight);\n }\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n\n const slots: CharSlot[] = [];\n let globalIndex = 0;\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let cx = canvasWidth / 2 - lineWidth / 2;\n\n for (const ch of Array.from(line)) {\n const cw = measureTextWidth(ctx, ch, fontSize, fontFamily, fontWeight);\n slots.push({\n ch,\n x: cx + cw / 2,\n y,\n globalIndex,\n });\n globalIndex++;\n cx += cw;\n }\n }\n\n ctx.restore();\n return { slots, fontSize, lineHeight };\n}\n\nfunction getCachedCharSlots(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number\n): CharSlotsLayout | null {\n const key = layoutCacheKey(layer, canvasWidth, canvasHeight);\n const cached = charSlotsCache.get(key);\n if (cached) {\n return cached;\n }\n const built = buildCharSlots(ctx, layer, canvasWidth, canvasHeight);\n if (built) {\n charSlotsCache.set(key, built);\n }\n return built;\n}\n\nfunction drawStaggerEntranceFrame(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n built: CharSlotsLayout,\n relativeFrame: number,\n fps: number,\n preset: CaptionStaggerPreset\n): void {\n const { slots, fontSize } = built;\n const fontConfig = layer.fontConfig!.textStyle!;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const strokeColor = fontConfig.strokeColor;\n const strokeWidth = fontConfig.strokeWidth || 0;\n\n const scalePx = fontSize / FONT_REF_PX;\n const staggerMs = DEFAULT_STAGGER_MS;\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const tMsGlobal = (relativeFrame / fps) * 1000;\n\n for (const slot of slots) {\n let p: number;\n if (preset === 'typewriter') {\n const startMs = slot.globalIndex * staggerMs;\n p = tMsGlobal >= startMs ? 1 : 0;\n } else {\n p = charProgress(relativeFrame, fps, slot.globalIndex, staggerMs, DEFAULT_DURATION_MS);\n }\n if (p <= 0 && preset !== 'blur') continue;\n\n const slidePx = SLIDE_BASE_PX * scalePx;\n const spreadPx = LETTER_SPREAD_BASE_PX * scalePx;\n\n ctx.save();\n\n let opacity = p;\n let tx = 0;\n let ty = 0;\n let rot = 0;\n let sc = 1;\n let sy = 1;\n let blurPx = 0;\n\n switch (preset) {\n case 'fade':\n opacity = p;\n break;\n case 'slideUp':\n opacity = p;\n ty = (1 - p) * slidePx;\n break;\n case 'scale':\n opacity = p;\n sc = Math.max(0.04, p);\n break;\n case 'rotateScale':\n opacity = p;\n rot = ((1 - p) * 45 * Math.PI) / 180;\n sc = 0.5 + p * 0.5;\n break;\n case 'blur':\n opacity = p;\n blurPx = (1 - p) * BLUR_START_PX;\n break;\n case 'flip3d':\n opacity = p;\n sy = Math.max(0.04, p);\n sc = 1;\n break;\n case 'typewriter':\n opacity = p >= 1 ? 1 : p;\n break;\n case 'letterSpread':\n opacity = p;\n tx = (1 - p) * spreadPx * slot.globalIndex;\n break;\n default:\n break;\n }\n\n ctx.globalAlpha = opacity;\n if (blurPx > 0.01) {\n ctx.filter = `blur(${blurPx}px)`;\n }\n\n ctx.translate(slot.x + tx, slot.y + ty);\n ctx.rotate(rot);\n if (preset === 'flip3d') {\n ctx.scale(1, sy);\n } else {\n ctx.scale(sc, sc);\n }\n\n if (strokeColor && strokeWidth > 0) {\n ctx.strokeStyle = strokeColor;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(slot.ch, 0, 0);\n }\n ctx.fillStyle = fill;\n ctx.fillText(slot.ch, 0, 0);\n\n ctx.restore();\n }\n\n ctx.restore();\n}\n\nexport function clearCaptionStaggerCache(): void {\n charSlotsCache.clear();\n for (const bitmap of staggerFinalRasterCache.values()) {\n bitmap.close();\n }\n staggerFinalRasterCache.clear();\n}\n\n/**\n * Per-character stagger entrance aligned with medeo-web preview (anime.js easeOutExpo, 800ms, 50ms stagger).\n */\nexport function renderCaptionStaggerEntrance(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number,\n preset: CaptionStaggerPreset\n): void {\n const built = getCachedCharSlots(ctx, layer, canvasWidth, canvasHeight);\n if (!built) return;\n\n const endMs = staggerEntranceEndMs(built.slots.length, preset);\n const endFrame = Math.ceil((endMs / 1000) * fps);\n\n if (relativeFrame >= endFrame) {\n const rasterKey = staggerRasterKey(layer, canvasWidth, canvasHeight, preset);\n let bitmap = staggerFinalRasterCache.get(rasterKey);\n if (!bitmap) {\n const offscreen = new OffscreenCanvas(canvasWidth, canvasHeight);\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n drawStaggerEntranceFrame(ctx, layer, built, endFrame, fps, preset);\n return;\n }\n drawStaggerEntranceFrame(offCtx, layer, built, endFrame, fps, preset);\n bitmap = offscreen.transferToImageBitmap();\n staggerFinalRasterCache.set(rasterKey, bitmap);\n }\n ctx.drawImage(bitmap, 0, 0);\n return;\n }\n\n drawStaggerEntranceFrame(ctx, layer, built, relativeFrame, fps, preset);\n}\n"],"names":[],"mappings":"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"}
@@ -28,7 +28,7 @@ function renderCharacterKTV(ctx, layer, canvasWidth, canvasHeight, relativeFrame
28
28
  const fontFamily = fontConfig.fontFamily;
29
29
  const fontWeight = fontConfig.fontWeight;
30
30
  const baseFill = fontConfig.fill;
31
- const stroke = fontConfig.stroke;
31
+ const strokeColor = fontConfig.strokeColor;
32
32
  const strokeWidth = fontConfig.strokeWidth || 0;
33
33
  const lineHeight = fontConfig.lineHeight || 1.2;
34
34
  const highlightFill = layer.animation?.highlightTextStyle?.fill || "rgb(255, 215, 0)";
@@ -112,8 +112,8 @@ function renderCharacterKTV(ctx, layer, canvasWidth, canvasHeight, relativeFrame
112
112
  } else {
113
113
  ctx.shadowBlur = 0;
114
114
  }
115
- if (stroke && strokeWidth > 0) {
116
- ctx.strokeStyle = stroke;
115
+ if (strokeColor && strokeWidth > 0) {
116
+ ctx.strokeStyle = strokeColor;
117
117
  ctx.lineWidth = strokeWidth;
118
118
  ctx.strokeText(char, currentX, y);
119
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"character-ktv-renderer.js","sources":["../../../../src/stages/compose/text-renderers/character-ktv-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { wrapText } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface CharacterTiming {\n char: string;\n index: number;\n startFrame: number;\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderCharacterKTV(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const baseFill = fontConfig.fill;\n const stroke = fontConfig.stroke;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightFill = layer.animation?.highlightTextStyle?.fill || 'rgb(255, 215, 0)';\n const glowColor = layer.animation?.glowColor || '#ffffff';\n const glowIntensity = layer.animation?.glowIntensity || 3;\n const transitionFrames = layer.animation?.transitionFrames || 10;\n\n const maxWidth = canvasWidth * 0.9;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const characterTimings: CharacterTiming[] = [];\n\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n let charIndex = 0;\n\n for (let wordIndex = 0; wordIndex < layer.wordTimings.length; wordIndex++) {\n const word = layer.wordTimings[wordIndex]!;\n const wordChars = word.text.split('');\n const wordStartFrame = usToFrame(word.startUs, fps);\n const wordEndFrame = usToFrame(word.endUs, fps);\n const framesPerChar =\n wordChars.length > 0 ? (wordEndFrame - wordStartFrame) / wordChars.length : 0;\n\n for (let i = 0; i < wordChars.length; i++) {\n const charStartFrame = Math.floor(wordStartFrame + i * framesPerChar);\n characterTimings.push({\n char: wordChars[i]!,\n index: charIndex,\n startFrame: charStartFrame,\n });\n charIndex++;\n }\n\n if (needsSpace && wordIndex < layer.wordTimings.length - 1) {\n const nextWord = layer.wordTimings[wordIndex + 1];\n const nextWordFirstChar = nextWord?.text?.[0] || '';\n const isPunctuation = /[.,!?;:)]/.test(nextWordFirstChar);\n\n if (!isPunctuation) {\n const spaceStartFrame = characterTimings[characterTimings.length - 1]?.startFrame || 0;\n characterTimings.push({\n char: ' ',\n index: charIndex,\n startFrame: spaceStartFrame,\n });\n charIndex++;\n }\n }\n }\n } else {\n const totalFrames = 100;\n const chars = text.split('');\n const framesPerChar = chars.length > 0 ? totalFrames / chars.length : 0;\n\n for (let i = 0; i < chars.length; i++) {\n characterTimings.push({\n char: chars[i]!,\n index: i,\n startFrame: Math.floor(i * framesPerChar),\n });\n }\n }\n\n const fullText = characterTimings.map((ct) => ct.char).join('');\n const fullTextLines = wrapText(ctx, fullText, maxWidth, fontSize, fontFamily, fontWeight);\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const totalHeight = fullTextLines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n let charIndexInText = 0;\n\n for (let lineIndex = 0; lineIndex < fullTextLines.length; lineIndex++) {\n const line = fullTextLines[lineIndex]!;\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]!;\n const timing = characterTimings[charIndexInText];\n\n if (timing) {\n const hasScanned = relativeFrame >= timing.startFrame;\n const isCurrentlySinging =\n hasScanned && relativeFrame < timing.startFrame + transitionFrames;\n\n ctx.fillStyle = hasScanned ? highlightFill : baseFill;\n\n if (isCurrentlySinging) {\n ctx.shadowColor = glowColor;\n ctx.shadowBlur = glowIntensity * 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n if (stroke && strokeWidth > 0) {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(char, currentX, y);\n }\n ctx.fillText(char, currentX, y);\n ctx.shadowBlur = 0;\n }\n\n currentX += measureTextWidth(ctx, char, fontSize, fontFamily, fontWeight);\n charIndexInText++;\n }\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":";;;AAWA,SAAS,UAAU,IAAY,KAAqB;AAClD,SAAO,KAAK,MAAM,MAAM,MAAU,IAAI;AACxC;AAEA,SAAS,mBACP,cACA,aACA,gBAUQ;AACR,MAAI,CAAC,gBAAgB;AACnB,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,MAAI,eAAe,KAAK;AACtB,UAAM,aAAa,WAAW,eAAe,GAAG,IAAI;AACpD,WAAO,eAAe;AAAA,EACxB;AAEA,MAAI,eAAe,QAAQ;AACzB,UAAM,gBAAgB,WAAW,eAAe,MAAM,IAAI;AAC1D,WAAO,gBAAgB,IAAI,iBAAiB;AAAA,EAC9C;AAEA,MAAI,eAAe,mBAAmB,YAAY,eAAe,eAAe,UAAU;AACxF,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,SAAO,eAAe,IAAI,cAAc;AAC1C;AAEO,SAAS,mBACd,KACA,OACA,aACA,cACA,eACA,MAAc,IACR;AACN,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,WAAY;AAEjB,QAAM,WAAW,WAAW;AAC5B,QAAM,aAAa,WAAW;AAC9B,QAAM,aAAa,WAAW;AAC9B,QAAM,WAAW,WAAW;AAC5B,QAAM,SAAS,WAAW;AAC1B,QAAM,cAAc,WAAW,eAAe;AAC9C,QAAM,aAAa,WAAW,cAAc;AAE5C,QAAM,gBAAgB,MAAM,WAAW,oBAAoB,QAAQ;AACnE,QAAM,YAAY,MAAM,WAAW,aAAa;AAChD,QAAM,gBAAgB,MAAM,WAAW,iBAAiB;AACxD,QAAM,mBAAmB,MAAM,WAAW,oBAAoB;AAE9D,QAAM,WAAW,cAAc;AAC/B,QAAM,OAAO,kBAAkB,MAAM,MAAM,MAAM,UAAU;AAC3D,QAAM,aAAa,uBAAuB,MAAM,cAAc,SAAS,IAAI;AAC3E,QAAM,mBAAsC,CAAA;AAE5C,MAAI,MAAM,eAAe,MAAM,YAAY,SAAS,GAAG;AACrD,QAAI,YAAY;AAEhB,aAAS,YAAY,GAAG,YAAY,MAAM,YAAY,QAAQ,aAAa;AACzE,YAAM,OAAO,MAAM,YAAY,SAAS;AACxC,YAAM,YAAY,KAAK,KAAK,MAAM,EAAE;AACpC,YAAM,iBAAiB,UAAU,KAAK,SAAS,GAAG;AAClD,YAAM,eAAe,UAAU,KAAK,OAAO,GAAG;AAC9C,YAAM,gBACJ,UAAU,SAAS,KAAK,eAAe,kBAAkB,UAAU,SAAS;AAE9E,eAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,cAAM,iBAAiB,KAAK,MAAM,iBAAiB,IAAI,aAAa;AACpE,yBAAiB,KAAK;AAAA,UACpB,MAAM,UAAU,CAAC;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,cAAc,YAAY,MAAM,YAAY,SAAS,GAAG;AAC1D,cAAM,WAAW,MAAM,YAAY,YAAY,CAAC;AAChD,cAAM,oBAAoB,UAAU,OAAO,CAAC,KAAK;AACjD,cAAM,gBAAgB,YAAY,KAAK,iBAAiB;AAExD,YAAI,CAAC,eAAe;AAClB,gBAAM,kBAAkB,iBAAiB,iBAAiB,SAAS,CAAC,GAAG,cAAc;AACrF,2BAAiB,KAAK;AAAA,YACpB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,UAAA,CACb;AACD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM,cAAc;AACpB,UAAM,QAAQ,KAAK,MAAM,EAAE;AAC3B,UAAM,gBAAgB,MAAM,SAAS,IAAI,cAAc,MAAM,SAAS;AAEtE,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,uBAAiB,KAAK;AAAA,QACpB,MAAM,MAAM,CAAC;AAAA,QACb,OAAO;AAAA,QACP,YAAY,KAAK,MAAM,IAAI,aAAa;AAAA,MAAA,CACzC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,WAAW,iBAAiB,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,KAAK,EAAE;AAC9D,QAAM,gBAAgB,SAAS,KAAK,UAAU,UAAU,UAAU,YAAY,UAAU;AAExF,MAAI,KAAA;AACJ,MAAI,OAAO,GAAG,UAAU,IAAI,QAAQ,MAAM,UAAU;AACpD,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,QAAM,cAAc,cAAc,SAAS,WAAW;AACtD,QAAM,SAAS,mBAAmB,cAAc,aAAa,MAAM,YAAY,cAAc;AAE7F,MAAI,kBAAkB;AAEtB,WAAS,YAAY,GAAG,YAAY,cAAc,QAAQ,aAAa;AACrE,UAAM,OAAO,cAAc,SAAS;AACpC,UAAM,IAAI,SAAS,YAAY,WAAW,aAAa,WAAW;AAClE,UAAM,YAAY,iBAAiB,KAAK,MAAM,UAAU,YAAY,UAAU;AAC9E,QAAI,WAAW,cAAc,IAAI,YAAY;AAE7C,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,SAAS,iBAAiB,eAAe;AAE/C,UAAI,QAAQ;AACV,cAAM,aAAa,iBAAiB,OAAO;AAC3C,cAAM,qBACJ,cAAc,gBAAgB,OAAO,aAAa;AAEpD,YAAI,YAAY,aAAa,gBAAgB;AAE7C,YAAI,oBAAoB;AACtB,cAAI,cAAc;AAClB,cAAI,aAAa,gBAAgB;AAAA,QACnC,OAAO;AACL,cAAI,aAAa;AAAA,QACnB;AAEA,YAAI,UAAU,cAAc,GAAG;AAC7B,cAAI,cAAc;AAClB,cAAI,YAAY;AAChB,cAAI,WAAW,MAAM,UAAU,CAAC;AAAA,QAClC;AACA,YAAI,SAAS,MAAM,UAAU,CAAC;AAC9B,YAAI,aAAa;AAAA,MACnB;AAEA,kBAAY,iBAAiB,KAAK,MAAM,UAAU,YAAY,UAAU;AACxE;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAA;AACN;"}
1
+ {"version":3,"file":"character-ktv-renderer.js","sources":["../../../../src/stages/compose/text-renderers/character-ktv-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { wrapText } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface CharacterTiming {\n char: string;\n index: number;\n startFrame: number;\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderCharacterKTV(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const baseFill = fontConfig.fill;\n const strokeColor = fontConfig.strokeColor;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightFill = layer.animation?.highlightTextStyle?.fill || 'rgb(255, 215, 0)';\n const glowColor = layer.animation?.glowColor || '#ffffff';\n const glowIntensity = layer.animation?.glowIntensity || 3;\n const transitionFrames = layer.animation?.transitionFrames || 10;\n\n const maxWidth = canvasWidth * 0.9;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const characterTimings: CharacterTiming[] = [];\n\n if (layer.wordTimings && layer.wordTimings.length > 0) {\n let charIndex = 0;\n\n for (let wordIndex = 0; wordIndex < layer.wordTimings.length; wordIndex++) {\n const word = layer.wordTimings[wordIndex]!;\n const wordChars = word.text.split('');\n const wordStartFrame = usToFrame(word.startUs, fps);\n const wordEndFrame = usToFrame(word.endUs, fps);\n const framesPerChar =\n wordChars.length > 0 ? (wordEndFrame - wordStartFrame) / wordChars.length : 0;\n\n for (let i = 0; i < wordChars.length; i++) {\n const charStartFrame = Math.floor(wordStartFrame + i * framesPerChar);\n characterTimings.push({\n char: wordChars[i]!,\n index: charIndex,\n startFrame: charStartFrame,\n });\n charIndex++;\n }\n\n if (needsSpace && wordIndex < layer.wordTimings.length - 1) {\n const nextWord = layer.wordTimings[wordIndex + 1];\n const nextWordFirstChar = nextWord?.text?.[0] || '';\n const isPunctuation = /[.,!?;:)]/.test(nextWordFirstChar);\n\n if (!isPunctuation) {\n const spaceStartFrame = characterTimings[characterTimings.length - 1]?.startFrame || 0;\n characterTimings.push({\n char: ' ',\n index: charIndex,\n startFrame: spaceStartFrame,\n });\n charIndex++;\n }\n }\n }\n } else {\n const totalFrames = 100;\n const chars = text.split('');\n const framesPerChar = chars.length > 0 ? totalFrames / chars.length : 0;\n\n for (let i = 0; i < chars.length; i++) {\n characterTimings.push({\n char: chars[i]!,\n index: i,\n startFrame: Math.floor(i * framesPerChar),\n });\n }\n }\n\n const fullText = characterTimings.map((ct) => ct.char).join('');\n const fullTextLines = wrapText(ctx, fullText, maxWidth, fontSize, fontFamily, fontWeight);\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n const totalHeight = fullTextLines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n let charIndexInText = 0;\n\n for (let lineIndex = 0; lineIndex < fullTextLines.length; lineIndex++) {\n const line = fullTextLines[lineIndex]!;\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]!;\n const timing = characterTimings[charIndexInText];\n\n if (timing) {\n const hasScanned = relativeFrame >= timing.startFrame;\n const isCurrentlySinging =\n hasScanned && relativeFrame < timing.startFrame + transitionFrames;\n\n ctx.fillStyle = hasScanned ? highlightFill : baseFill;\n\n if (isCurrentlySinging) {\n ctx.shadowColor = glowColor;\n ctx.shadowBlur = glowIntensity * 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n if (strokeColor && strokeWidth > 0) {\n ctx.strokeStyle = strokeColor;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(char, currentX, y);\n }\n ctx.fillText(char, currentX, y);\n ctx.shadowBlur = 0;\n }\n\n currentX += measureTextWidth(ctx, char, fontSize, fontFamily, fontWeight);\n charIndexInText++;\n }\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":";;;AAWA,SAAS,UAAU,IAAY,KAAqB;AAClD,SAAO,KAAK,MAAM,MAAM,MAAU,IAAI;AACxC;AAEA,SAAS,mBACP,cACA,aACA,gBAUQ;AACR,MAAI,CAAC,gBAAgB;AACnB,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,MAAI,eAAe,KAAK;AACtB,UAAM,aAAa,WAAW,eAAe,GAAG,IAAI;AACpD,WAAO,eAAe;AAAA,EACxB;AAEA,MAAI,eAAe,QAAQ;AACzB,UAAM,gBAAgB,WAAW,eAAe,MAAM,IAAI;AAC1D,WAAO,gBAAgB,IAAI,iBAAiB;AAAA,EAC9C;AAEA,MAAI,eAAe,mBAAmB,YAAY,eAAe,eAAe,UAAU;AACxF,WAAO,eAAe,IAAI,cAAc;AAAA,EAC1C;AAEA,SAAO,eAAe,IAAI,cAAc;AAC1C;AAEO,SAAS,mBACd,KACA,OACA,aACA,cACA,eACA,MAAc,IACR;AACN,QAAM,aAAa,MAAM,YAAY;AACrC,MAAI,CAAC,WAAY;AAEjB,QAAM,WAAW,WAAW;AAC5B,QAAM,aAAa,WAAW;AAC9B,QAAM,aAAa,WAAW;AAC9B,QAAM,WAAW,WAAW;AAC5B,QAAM,cAAc,WAAW;AAC/B,QAAM,cAAc,WAAW,eAAe;AAC9C,QAAM,aAAa,WAAW,cAAc;AAE5C,QAAM,gBAAgB,MAAM,WAAW,oBAAoB,QAAQ;AACnE,QAAM,YAAY,MAAM,WAAW,aAAa;AAChD,QAAM,gBAAgB,MAAM,WAAW,iBAAiB;AACxD,QAAM,mBAAmB,MAAM,WAAW,oBAAoB;AAE9D,QAAM,WAAW,cAAc;AAC/B,QAAM,OAAO,kBAAkB,MAAM,MAAM,MAAM,UAAU;AAC3D,QAAM,aAAa,uBAAuB,MAAM,cAAc,SAAS,IAAI;AAC3E,QAAM,mBAAsC,CAAA;AAE5C,MAAI,MAAM,eAAe,MAAM,YAAY,SAAS,GAAG;AACrD,QAAI,YAAY;AAEhB,aAAS,YAAY,GAAG,YAAY,MAAM,YAAY,QAAQ,aAAa;AACzE,YAAM,OAAO,MAAM,YAAY,SAAS;AACxC,YAAM,YAAY,KAAK,KAAK,MAAM,EAAE;AACpC,YAAM,iBAAiB,UAAU,KAAK,SAAS,GAAG;AAClD,YAAM,eAAe,UAAU,KAAK,OAAO,GAAG;AAC9C,YAAM,gBACJ,UAAU,SAAS,KAAK,eAAe,kBAAkB,UAAU,SAAS;AAE9E,eAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,cAAM,iBAAiB,KAAK,MAAM,iBAAiB,IAAI,aAAa;AACpE,yBAAiB,KAAK;AAAA,UACpB,MAAM,UAAU,CAAC;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,UAAI,cAAc,YAAY,MAAM,YAAY,SAAS,GAAG;AAC1D,cAAM,WAAW,MAAM,YAAY,YAAY,CAAC;AAChD,cAAM,oBAAoB,UAAU,OAAO,CAAC,KAAK;AACjD,cAAM,gBAAgB,YAAY,KAAK,iBAAiB;AAExD,YAAI,CAAC,eAAe;AAClB,gBAAM,kBAAkB,iBAAiB,iBAAiB,SAAS,CAAC,GAAG,cAAc;AACrF,2BAAiB,KAAK;AAAA,YACpB,MAAM;AAAA,YACN,OAAO;AAAA,YACP,YAAY;AAAA,UAAA,CACb;AACD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM,cAAc;AACpB,UAAM,QAAQ,KAAK,MAAM,EAAE;AAC3B,UAAM,gBAAgB,MAAM,SAAS,IAAI,cAAc,MAAM,SAAS;AAEtE,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,uBAAiB,KAAK;AAAA,QACpB,MAAM,MAAM,CAAC;AAAA,QACb,OAAO;AAAA,QACP,YAAY,KAAK,MAAM,IAAI,aAAa;AAAA,MAAA,CACzC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,WAAW,iBAAiB,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,KAAK,EAAE;AAC9D,QAAM,gBAAgB,SAAS,KAAK,UAAU,UAAU,UAAU,YAAY,UAAU;AAExF,MAAI,KAAA;AACJ,MAAI,OAAO,GAAG,UAAU,IAAI,QAAQ,MAAM,UAAU;AACpD,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,QAAM,cAAc,cAAc,SAAS,WAAW;AACtD,QAAM,SAAS,mBAAmB,cAAc,aAAa,MAAM,YAAY,cAAc;AAE7F,MAAI,kBAAkB;AAEtB,WAAS,YAAY,GAAG,YAAY,cAAc,QAAQ,aAAa;AACrE,UAAM,OAAO,cAAc,SAAS;AACpC,UAAM,IAAI,SAAS,YAAY,WAAW,aAAa,WAAW;AAClE,UAAM,YAAY,iBAAiB,KAAK,MAAM,UAAU,YAAY,UAAU;AAC9E,QAAI,WAAW,cAAc,IAAI,YAAY;AAE7C,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,SAAS,iBAAiB,eAAe;AAE/C,UAAI,QAAQ;AACV,cAAM,aAAa,iBAAiB,OAAO;AAC3C,cAAM,qBACJ,cAAc,gBAAgB,OAAO,aAAa;AAEpD,YAAI,YAAY,aAAa,gBAAgB;AAE7C,YAAI,oBAAoB;AACtB,cAAI,cAAc;AAClB,cAAI,aAAa,gBAAgB;AAAA,QACnC,OAAO;AACL,cAAI,aAAa;AAAA,QACnB;AAEA,YAAI,eAAe,cAAc,GAAG;AAClC,cAAI,cAAc;AAClB,cAAI,YAAY;AAChB,cAAI,WAAW,MAAM,UAAU,CAAC;AAAA,QAClC;AACA,YAAI,SAAS,MAAM,UAAU,CAAC;AAC9B,YAAI,aAAa;AAAA,MACnB;AAEA,kBAAY,iBAAiB,KAAK,MAAM,UAAU,YAAY,UAAU;AACxE;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAA;AACN;"}
@@ -29,11 +29,11 @@ function renderWordByWord(ctx, layer, canvasWidth, canvasHeight, relativeFrame,
29
29
  const fontFamily = fontConfig.fontFamily;
30
30
  const fontWeight = fontConfig.fontWeight;
31
31
  const fill = fontConfig.fill;
32
- const stroke = fontConfig.stroke;
32
+ const strokeColor = fontConfig.strokeColor;
33
33
  const strokeWidth = fontConfig.strokeWidth || 0;
34
34
  const lineHeight = fontConfig.lineHeight || 1.2;
35
35
  const highlightFill = layer.animation?.highlightTextStyle?.fill || "rgb(255, 215, 0)";
36
- const highlightStroke = layer.animation?.highlightTextStyle?.stroke || stroke;
36
+ const highlightStroke = layer.animation?.highlightTextStyle?.strokeColor || strokeColor;
37
37
  const maxWidth = canvasWidth * 0.64;
38
38
  const text = getLetterCaseText(layer.text, layer.letterCase);
39
39
  const needsSpace = needsSpaceBetweenWords(layer.localeCode || "en-US", text);
@@ -84,7 +84,7 @@ function renderWordByWord(ctx, layer, canvasWidth, canvasHeight, relativeFrame,
84
84
  ctx.lineCap = "round";
85
85
  for (const wordPos of wordPositions) {
86
86
  let currentFill = fill;
87
- let currentStroke = stroke;
87
+ let currentStroke = strokeColor;
88
88
  if (wordPos.timing) {
89
89
  const { startFrame, endFrame } = wordPos.timing;
90
90
  if (relativeFrame >= startFrame && relativeFrame <= endFrame) {
@@ -98,8 +98,8 @@ function renderWordByWord(ctx, layer, canvasWidth, canvasHeight, relativeFrame,
98
98
  }
99
99
  );
100
100
  currentFill = interpolateColor(fill, highlightFill, transitionProgressIn);
101
- if (stroke && highlightStroke) {
102
- currentStroke = interpolateColor(stroke, highlightStroke, transitionProgressIn);
101
+ if (strokeColor && highlightStroke) {
102
+ currentStroke = interpolateColor(strokeColor, highlightStroke, transitionProgressIn);
103
103
  }
104
104
  } else if (relativeFrame > endFrame) {
105
105
  const transitionProgressOut = interpolate(relativeFrame, [endFrame, endFrame + 3], [1, 0], {
@@ -107,8 +107,8 @@ function renderWordByWord(ctx, layer, canvasWidth, canvasHeight, relativeFrame,
107
107
  extrapolateRight: "clamp"
108
108
  });
109
109
  currentFill = interpolateColor(fill, highlightFill, transitionProgressOut);
110
- if (stroke && highlightStroke) {
111
- currentStroke = interpolateColor(stroke, highlightStroke, transitionProgressOut);
110
+ if (strokeColor && highlightStroke) {
111
+ currentStroke = interpolateColor(strokeColor, highlightStroke, transitionProgressOut);
112
112
  }
113
113
  }
114
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"word-by-word-renderer.js","sources":["../../../../src/stages/compose/text-renderers/word-by-word-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { formEvenLinesWithWords } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { interpolate, interpolateColor } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface WordPosition {\n text: string;\n x: number;\n y: number;\n lineIndex: number;\n wordIndex: number;\n timing?: { startFrame: number; endFrame: number };\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderWordByWord(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const stroke = fontConfig.stroke;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightFill = layer.animation?.highlightTextStyle?.fill || 'rgb(255, 215, 0)';\n const highlightStroke = layer.animation?.highlightTextStyle?.stroke || stroke;\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = text.split(needsSpace ? /\\s+/ : '');\n const lines = formEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n\n const wordPositions: WordPosition[] = [];\n let wordIndex = 0;\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const lineWords = line.split(needsSpace ? /\\s+/ : '');\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (const word of lineWords) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n const wordTimingUs = layer.wordTimings?.[wordIndex];\n\n const wordTiming = wordTimingUs\n ? {\n startFrame: usToFrame(wordTimingUs.startUs, fps),\n endFrame: usToFrame(wordTimingUs.endUs, fps),\n }\n : undefined;\n\n wordPositions.push({\n text: word,\n x: currentX + wordWidth / 2,\n y,\n lineIndex,\n wordIndex,\n timing: wordTiming,\n });\n\n currentX +=\n wordWidth + (needsSpace ? measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight) : 0);\n wordIndex++;\n }\n }\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n for (const wordPos of wordPositions) {\n let currentFill = fill;\n let currentStroke = stroke;\n\n if (wordPos.timing) {\n const { startFrame, endFrame } = wordPos.timing;\n\n if (relativeFrame >= startFrame && relativeFrame <= endFrame) {\n const transitionProgressIn = interpolate(\n relativeFrame,\n [startFrame, startFrame + 3],\n [0, 1],\n {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n }\n );\n\n currentFill = interpolateColor(fill, highlightFill, transitionProgressIn);\n if (stroke && highlightStroke) {\n currentStroke = interpolateColor(stroke, highlightStroke, transitionProgressIn);\n }\n } else if (relativeFrame > endFrame) {\n const transitionProgressOut = interpolate(relativeFrame, [endFrame, endFrame + 3], [1, 0], {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n\n currentFill = interpolateColor(fill, highlightFill, transitionProgressOut);\n if (stroke && highlightStroke) {\n currentStroke = interpolateColor(stroke, highlightStroke, transitionProgressOut);\n }\n }\n }\n\n if (currentStroke && strokeWidth > 0) {\n ctx.strokeStyle = currentStroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(wordPos.text, wordPos.x, wordPos.y);\n }\n ctx.fillStyle = currentFill;\n ctx.fillText(wordPos.text, wordPos.x, wordPos.y);\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"word-by-word-renderer.js","sources":["../../../../src/stages/compose/text-renderers/word-by-word-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { formEvenLinesWithWords } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { interpolate, interpolateColor } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface WordPosition {\n text: string;\n x: number;\n y: number;\n lineIndex: number;\n wordIndex: number;\n timing?: { startFrame: number; endFrame: number };\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderWordByWord(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const strokeColor = fontConfig.strokeColor;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightFill = layer.animation?.highlightTextStyle?.fill || 'rgb(255, 215, 0)';\n const highlightStroke = layer.animation?.highlightTextStyle?.strokeColor || strokeColor;\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = text.split(needsSpace ? /\\s+/ : '');\n const lines = formEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n\n const wordPositions: WordPosition[] = [];\n let wordIndex = 0;\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const lineWords = line.split(needsSpace ? /\\s+/ : '');\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (const word of lineWords) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n const wordTimingUs = layer.wordTimings?.[wordIndex];\n\n const wordTiming = wordTimingUs\n ? {\n startFrame: usToFrame(wordTimingUs.startUs, fps),\n endFrame: usToFrame(wordTimingUs.endUs, fps),\n }\n : undefined;\n\n wordPositions.push({\n text: word,\n x: currentX + wordWidth / 2,\n y,\n lineIndex,\n wordIndex,\n timing: wordTiming,\n });\n\n currentX +=\n wordWidth + (needsSpace ? measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight) : 0);\n wordIndex++;\n }\n }\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n for (const wordPos of wordPositions) {\n let currentFill = fill;\n let currentStroke = strokeColor;\n\n if (wordPos.timing) {\n const { startFrame, endFrame } = wordPos.timing;\n\n if (relativeFrame >= startFrame && relativeFrame <= endFrame) {\n const transitionProgressIn = interpolate(\n relativeFrame,\n [startFrame, startFrame + 3],\n [0, 1],\n {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n }\n );\n\n currentFill = interpolateColor(fill, highlightFill, transitionProgressIn);\n if (strokeColor && highlightStroke) {\n currentStroke = interpolateColor(strokeColor, highlightStroke, transitionProgressIn);\n }\n } else if (relativeFrame > endFrame) {\n const transitionProgressOut = interpolate(relativeFrame, [endFrame, endFrame + 3], [1, 0], {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n\n currentFill = interpolateColor(fill, highlightFill, transitionProgressOut);\n if (strokeColor && highlightStroke) {\n currentStroke = interpolateColor(strokeColor, highlightStroke, transitionProgressOut);\n }\n }\n }\n\n if (currentStroke && strokeWidth > 0) {\n ctx.strokeStyle = currentStroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(wordPos.text, wordPos.x, wordPos.y);\n }\n ctx.fillStyle = currentFill;\n ctx.fillText(wordPos.text, wordPos.x, wordPos.y);\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":"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"}
@@ -29,7 +29,7 @@ function renderWordByWordFancy(ctx, layer, canvasWidth, canvasHeight, relativeFr
29
29
  const fontFamily = fontConfig.fontFamily;
30
30
  const fontWeight = fontConfig.fontWeight;
31
31
  const fill = fontConfig.fill;
32
- const stroke = fontConfig.stroke;
32
+ const strokeColor = fontConfig.strokeColor;
33
33
  const strokeWidth = fontConfig.strokeWidth || 0;
34
34
  const lineHeight = fontConfig.lineHeight || 1.2;
35
35
  const highlightBackgroundColor = layer.animation?.highlightColor || "rgb(255, 215, 0)";
@@ -119,8 +119,8 @@ function renderWordByWordFancy(ctx, layer, canvasWidth, canvasHeight, relativeFr
119
119
  ctx.restore();
120
120
  }
121
121
  }
122
- if (stroke && strokeWidth > 0) {
123
- ctx.strokeStyle = stroke;
122
+ if (strokeColor && strokeWidth > 0) {
123
+ ctx.strokeStyle = strokeColor;
124
124
  ctx.lineWidth = strokeWidth;
125
125
  ctx.strokeText(wordPos.text, wordPos.x, wordPos.y);
126
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"word-fancy-renderer.js","sources":["../../../../src/stages/compose/text-renderers/word-fancy-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { formEvenLinesWithWords } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { springEasing, interpolate } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface WordPosition {\n text: string;\n x: number;\n y: number;\n width: number;\n lineIndex: number;\n wordIndex: number;\n timing?: { startFrame: number; endFrame: number };\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderWordByWordFancy(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const stroke = fontConfig.stroke;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightBackgroundColor = layer.animation?.highlightColor || 'rgb(255, 215, 0)';\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = text.split(needsSpace ? /\\s+/ : '');\n const lines = formEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n\n const wordPositions: WordPosition[] = [];\n let wordIndex = 0;\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const lineWords = line.split(needsSpace ? /\\s+/ : '');\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (const word of lineWords) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n const wordTimingUs = layer.wordTimings?.[wordIndex];\n\n const wordTiming = wordTimingUs\n ? {\n startFrame: usToFrame(wordTimingUs.startUs, fps),\n endFrame: usToFrame(wordTimingUs.endUs, fps),\n }\n : undefined;\n\n wordPositions.push({\n text: word,\n x: currentX + wordWidth / 2,\n y,\n width: wordWidth,\n lineIndex,\n wordIndex,\n timing: wordTiming,\n });\n\n currentX +=\n wordWidth + (needsSpace ? measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight) : 0);\n wordIndex++;\n }\n }\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n for (const wordPos of wordPositions) {\n let backgroundOpacity = 0;\n let backgroundScale = 0.8;\n\n if (wordPos.timing) {\n const { startFrame, endFrame } = wordPos.timing;\n const preStartFrames = 3;\n const isActive = relativeFrame >= startFrame && relativeFrame <= endFrame;\n\n if (isActive) {\n const scaleSpringIn = springEasing(relativeFrame - (startFrame - preStartFrames), {\n damping: 6,\n mass: 0.35,\n stiffness: 200,\n overshootClamping: false,\n });\n\n const inProgress = interpolate(relativeFrame, [startFrame, startFrame + 1], [0, 1], {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n\n backgroundOpacity = 0.9 * inProgress;\n backgroundScale = 0.8 + scaleSpringIn * 0.45;\n } else if (relativeFrame > endFrame) {\n backgroundOpacity = 0;\n backgroundScale = 0.8;\n }\n\n if (backgroundOpacity > 0) {\n const padding = 8;\n const bgWidth = (wordPos.width + padding * 2) * backgroundScale;\n const bgHeight = (fontSize + padding) * backgroundScale;\n\n ctx.save();\n ctx.globalAlpha = backgroundOpacity;\n ctx.fillStyle = highlightBackgroundColor;\n ctx.beginPath();\n ctx.roundRect(wordPos.x - bgWidth / 2, wordPos.y - bgHeight / 2, bgWidth, bgHeight, 8);\n ctx.fill();\n ctx.restore();\n }\n }\n\n if (stroke && strokeWidth > 0) {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(wordPos.text, wordPos.x, wordPos.y);\n }\n ctx.fillStyle = fill;\n ctx.fillText(wordPos.text, wordPos.x, wordPos.y);\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"word-fancy-renderer.js","sources":["../../../../src/stages/compose/text-renderers/word-fancy-renderer.ts"],"sourcesContent":["import type { TextLayer } from '../types';\nimport { formEvenLinesWithWords } from '../text-utils/text-wrapper';\nimport { getLetterCaseText, measureTextWidth } from '../text-utils/text-metrics';\nimport { springEasing, interpolate } from './animation-utils';\nimport { needsSpaceBetweenWords } from '../text-utils/locale-detector';\n\ninterface WordPosition {\n text: string;\n x: number;\n y: number;\n width: number;\n lineIndex: number;\n wordIndex: number;\n timing?: { startFrame: number; endFrame: number };\n}\n\nfunction usToFrame(us: number, fps: number): number {\n return Math.floor(us / (1000000 / fps));\n}\n\nfunction calculateYPosition(\n canvasHeight: number,\n totalHeight: number,\n globalPosition?: {\n position?: 'absolute';\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n display?: string;\n alignItems?: string;\n justifyContent?: string;\n }\n): number {\n if (!globalPosition) {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n if (globalPosition.top) {\n const topPercent = parseFloat(globalPosition.top) / 100;\n return canvasHeight * topPercent;\n }\n\n if (globalPosition.bottom) {\n const bottomPercent = parseFloat(globalPosition.bottom) / 100;\n return canvasHeight * (1 - bottomPercent) - totalHeight;\n }\n\n if (globalPosition.justifyContent === 'center' || globalPosition.alignItems === 'center') {\n return canvasHeight / 2 - totalHeight / 2;\n }\n\n return canvasHeight / 2 - totalHeight / 2;\n}\n\nexport function renderWordByWordFancy(\n ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,\n layer: TextLayer,\n canvasWidth: number,\n canvasHeight: number,\n relativeFrame: number,\n fps: number = 30\n): void {\n const fontConfig = layer.fontConfig?.textStyle;\n if (!fontConfig) return;\n\n const fontSize = fontConfig.fontSize;\n const fontFamily = fontConfig.fontFamily;\n const fontWeight = fontConfig.fontWeight;\n const fill = fontConfig.fill;\n const strokeColor = fontConfig.strokeColor;\n const strokeWidth = fontConfig.strokeWidth || 0;\n const lineHeight = fontConfig.lineHeight || 1.2;\n\n const highlightBackgroundColor = layer.animation?.highlightColor || 'rgb(255, 215, 0)';\n\n const maxWidth = canvasWidth * 0.64;\n const text = getLetterCaseText(layer.text, layer.letterCase);\n const needsSpace = needsSpaceBetweenWords(layer.localeCode || 'en-US', text);\n const words = text.split(needsSpace ? /\\s+/ : '');\n const lines = formEvenLinesWithWords(\n ctx,\n words,\n maxWidth,\n fontSize,\n needsSpace,\n fontFamily,\n fontWeight\n );\n\n const wordPositions: WordPosition[] = [];\n let wordIndex = 0;\n\n const totalHeight = lines.length * fontSize * lineHeight;\n const startY = calculateYPosition(canvasHeight, totalHeight, layer.fontConfig?.globalPosition);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex]!;\n const lineWords = line.split(needsSpace ? /\\s+/ : '');\n const y = startY + lineIndex * fontSize * lineHeight + fontSize / 2;\n\n const lineWidth = measureTextWidth(ctx, line, fontSize, fontFamily, fontWeight);\n let currentX = canvasWidth / 2 - lineWidth / 2;\n\n for (const word of lineWords) {\n const wordWidth = measureTextWidth(ctx, word, fontSize, fontFamily, fontWeight);\n const wordTimingUs = layer.wordTimings?.[wordIndex];\n\n const wordTiming = wordTimingUs\n ? {\n startFrame: usToFrame(wordTimingUs.startUs, fps),\n endFrame: usToFrame(wordTimingUs.endUs, fps),\n }\n : undefined;\n\n wordPositions.push({\n text: word,\n x: currentX + wordWidth / 2,\n y,\n width: wordWidth,\n lineIndex,\n wordIndex,\n timing: wordTiming,\n });\n\n currentX +=\n wordWidth + (needsSpace ? measureTextWidth(ctx, ' ', fontSize, fontFamily, fontWeight) : 0);\n wordIndex++;\n }\n }\n\n ctx.save();\n ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n\n for (const wordPos of wordPositions) {\n let backgroundOpacity = 0;\n let backgroundScale = 0.8;\n\n if (wordPos.timing) {\n const { startFrame, endFrame } = wordPos.timing;\n const preStartFrames = 3;\n const isActive = relativeFrame >= startFrame && relativeFrame <= endFrame;\n\n if (isActive) {\n const scaleSpringIn = springEasing(relativeFrame - (startFrame - preStartFrames), {\n damping: 6,\n mass: 0.35,\n stiffness: 200,\n overshootClamping: false,\n });\n\n const inProgress = interpolate(relativeFrame, [startFrame, startFrame + 1], [0, 1], {\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n\n backgroundOpacity = 0.9 * inProgress;\n backgroundScale = 0.8 + scaleSpringIn * 0.45;\n } else if (relativeFrame > endFrame) {\n backgroundOpacity = 0;\n backgroundScale = 0.8;\n }\n\n if (backgroundOpacity > 0) {\n const padding = 8;\n const bgWidth = (wordPos.width + padding * 2) * backgroundScale;\n const bgHeight = (fontSize + padding) * backgroundScale;\n\n ctx.save();\n ctx.globalAlpha = backgroundOpacity;\n ctx.fillStyle = highlightBackgroundColor;\n ctx.beginPath();\n ctx.roundRect(wordPos.x - bgWidth / 2, wordPos.y - bgHeight / 2, bgWidth, bgHeight, 8);\n ctx.fill();\n ctx.restore();\n }\n }\n\n if (strokeColor && strokeWidth > 0) {\n ctx.strokeStyle = strokeColor;\n ctx.lineWidth = strokeWidth;\n ctx.strokeText(wordPos.text, wordPos.x, wordPos.y);\n }\n ctx.fillStyle = fill;\n ctx.fillText(wordPos.text, wordPos.x, wordPos.y);\n }\n\n ctx.restore();\n}\n"],"names":[],"mappings":"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"}
@@ -45,7 +45,7 @@ export interface CaptionStyle {
45
45
  fontFamily: string;
46
46
  fontWeight: string | number;
47
47
  fill: string;
48
- stroke?: string;
48
+ strokeColor?: string;
49
49
  strokeWidth?: number;
50
50
  lineHeight: number;
51
51
  letterSpacing?: string | number;
@@ -1 +1 @@
1
- {"version":3,"file":"caption-layout.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/text-utils/caption-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAQ5F,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CACtD;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,GAAG,gBAAgB,CAAC,CAAC;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqFD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CA4E7E"}
1
+ {"version":3,"file":"caption-layout.d.ts","sourceRoot":"","sources":["../../../../src/stages/compose/text-utils/caption-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAQ5F,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CACtD;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,GAAG,gBAAgB,CAAC,CAAC;IACxF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqFD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CA4E7E"}