@cueframe/kernel 0.1.0 → 0.1.1
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.
- package/LICENSE +661 -0
- package/dist/_members/composition/ops/compositionOps.d.ts +1 -0
- package/dist/_members/composition/projection/behind-readiness.d.ts +127 -0
- package/dist/_members/composition/projection/index.d.ts +2 -0
- package/dist/_members/composition/projection/reframe-readiness.d.ts +156 -0
- package/dist/_members/composition/validators/composition.d.ts +42 -25
- package/dist/_members/composition/validators/render.d.ts +32 -0
- package/dist/_members/core/render/compositionRender.d.ts +17 -1
- package/dist/_members/core/render/types.d.ts +31 -0
- package/dist/authoring/build-tools.js +6 -6
- package/dist/authoring/context.js +4 -4
- package/dist/authoring/tools.js +5 -5
- package/dist/authoring.js +11 -9
- package/dist/{chunk-ZB3C3HM7.js → chunk-7FO3ZJ43.js} +3 -0
- package/dist/{chunk-ODL7GQJG.js → chunk-AP6ZIVAV.js} +2 -2
- package/dist/{chunk-IBYQG46O.js → chunk-AQBREB5O.js} +1 -1
- package/dist/{chunk-YIOY2XEO.js → chunk-DK2ODBNT.js} +15 -5
- package/dist/{chunk-JRQVPFLF.js → chunk-HY3RIEZW.js} +1 -1
- package/dist/{chunk-GEA7E2FL.js → chunk-JJ6XD7NG.js} +151 -1
- package/dist/{chunk-COYMZ76S.js → chunk-MBCXCJOE.js} +1 -1
- package/dist/{chunk-37NJ27UU.js → chunk-OCFDUCF4.js} +3 -0
- package/dist/{chunk-VOO5FQSS.js → chunk-PDYO3DOJ.js} +5 -5
- package/dist/{chunk-MXFAADCQ.js → chunk-PODIYN2K.js} +9 -2
- package/dist/{chunk-NFEGPWGS.js → chunk-TOJEXFXL.js} +1 -1
- package/dist/{chunk-LM4WDUQJ.js → chunk-UHGSZZDI.js} +1 -1
- package/dist/{chunk-XWKZB5U5.js → chunk-ZWMV6DD2.js} +24 -1
- package/dist/compose/renderer.js +3 -3
- package/dist/compose.js +6 -6
- package/dist/composition/graphics-plan.js +1 -1
- package/dist/composition/ops.js +3 -3
- package/dist/composition/primitive-catalog.js +1 -1
- package/dist/composition/projection.js +19 -3
- package/dist/composition/validators.js +6 -4
- package/dist/composition-ops.js +8 -6
- package/dist/composition.js +24 -6
- package/dist/{context-OKX3HHFS.js → context-YWUZDG5X.js} +4 -4
- package/dist/core/render.js +3 -3
- package/dist/core/select.js +2 -2
- package/dist/core.js +3 -3
- package/dist/errors.js +1 -1
- package/dist/mcp/adapter.js +1 -0
- package/dist/media-model/testing.js +1071 -718
- package/dist/nle-export/server.js +328 -130
- package/dist/nle-export.js +7 -6
- package/package.json +13 -14
- package/dist/_members/account-model/index.js +0 -4
- package/dist/_members/account-model/testing/conversationStoreContract.js +0 -168
- package/dist/_members/account-model/testing/index.js +0 -7
- package/dist/_members/account-model/testing/restorePointStoreContract.js +0 -69
- package/dist/_members/account-model/types/index.js +0 -1
- package/dist/_members/account-model/validators/contextFile.js +0 -18
- package/dist/_members/account-model/validators/conversation.js +0 -95
- package/dist/_members/account-model/validators/dashboardInsight.js +0 -16
- package/dist/_members/account-model/validators/index.js +0 -7
- package/dist/_members/account-model/validators/sessionToken.js +0 -49
- package/dist/_members/account-model/validators/uploadToken.js +0 -16
- package/dist/_members/account-model/validators/userSettings.js +0 -12
- package/dist/_members/account-model/validators/workingComposition.js +0 -20
- package/dist/_members/brand-derivation/constants.js +0 -20
- package/dist/_members/brand-derivation/contrastSafe.js +0 -114
- package/dist/_members/brand-derivation/index.js +0 -1
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js +0 -162
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js.map +0 -1
- package/dist/_members/cloud-import/index.js +0 -241
- package/dist/_members/cloud-import/index.js.map +0 -1
- package/dist/_members/cloud-import/multipartUpload.js +0 -7
- package/dist/_members/cloud-import/multipartUpload.js.map +0 -1
- package/dist/_members/composition/audio/audioMix.js +0 -61
- package/dist/_members/composition/audio/index.js +0 -5
- package/dist/_members/composition/audio/mixStaging.js +0 -195
- package/dist/_members/composition/audio/musicPack.generated.js +0 -136
- package/dist/_members/composition/audio/musicPack.js +0 -36
- package/dist/_members/composition/audio/pack.generated.js +0 -603
- package/dist/_members/composition/audio/pack.js +0 -48
- package/dist/_members/composition/audio/palette.js +0 -134
- package/dist/_members/composition/autozoom/index.js +0 -83
- package/dist/_members/composition/brand-kit.js +0 -45
- package/dist/_members/composition/brief/contract.js +0 -209
- package/dist/_members/composition/color-grade.js +0 -80
- package/dist/_members/composition/depth.js +0 -8
- package/dist/_members/composition/fixtures/index.js +0 -143
- package/dist/_members/composition/fixtures/vaayaErrored.d.ts +0 -25
- package/dist/_members/composition/fixtures/vaayaErrored.js +0 -203
- package/dist/_members/composition/fonts.js +0 -162
- package/dist/_members/composition/graphics-plan/compile.js +0 -221
- package/dist/_members/composition/graphics-plan/index.js +0 -10
- package/dist/_members/composition/graphics-plan/instantiateMotif.js +0 -44
- package/dist/_members/composition/graphics-plan/motion.js +0 -66
- package/dist/_members/composition/graphics-plan/schemas.js +0 -261
- package/dist/_members/composition/graphics-plan/solve.js +0 -145
- package/dist/_members/composition/index.js +0 -5
- package/dist/_members/composition/invariants/clipPlacement.js +0 -69
- package/dist/_members/composition/invariants/composition.js +0 -28
- package/dist/_members/composition/invariants/errors.js +0 -10
- package/dist/_members/composition/invariants/etag.js +0 -38
- package/dist/_members/composition/invariants/ids.js +0 -33
- package/dist/_members/composition/invariants/index.js +0 -9
- package/dist/_members/composition/invariants/keyframes.js +0 -45
- package/dist/_members/composition/invariants/markers.js +0 -29
- package/dist/_members/composition/invariants/time.js +0 -20
- package/dist/_members/composition/invariants/tracks.js +0 -304
- package/dist/_members/composition/ops/compositionOps.js +0 -1056
- package/dist/_members/composition/ops/headlessStore.js +0 -94
- package/dist/_members/composition/ops/index.js +0 -4
- package/dist/_members/composition/ops/selectors.js +0 -118
- package/dist/_members/composition/ops/timeline-word.js +0 -1
- package/dist/_members/composition/ops/transcript.js +0 -77
- package/dist/_members/composition/ops/types.js +0 -16
- package/dist/_members/composition/primitive-catalog/catalogQuery.js +0 -150
- package/dist/_members/composition/primitive-catalog/disabled.js +0 -32
- package/dist/_members/composition/primitive-catalog/generated/meta.js +0 -15161
- package/dist/_members/composition/primitive-catalog/index.js +0 -46
- package/dist/_members/composition/primitive-catalog/paramValidation.js +0 -231
- package/dist/_members/composition/primitive-catalog/projection.js +0 -164
- package/dist/_members/composition/primitive-catalog/types.js +0 -14
- package/dist/_members/composition/projection/behind-placement.js +0 -62
- package/dist/_members/composition/projection/brandKit.js +0 -482
- package/dist/_members/composition/projection/cardTokens.js +0 -209
- package/dist/_members/composition/projection/composition-floor.js +0 -582
- package/dist/_members/composition/projection/cropProjection.js +0 -257
- package/dist/_members/composition/projection/index.js +0 -7
- package/dist/_members/composition/projection/occlusion-budget.js +0 -104
- package/dist/_members/composition/projection/safe-zones.js +0 -99
- package/dist/_members/composition/reframe/aspectLoss.js +0 -115
- package/dist/_members/composition/reframe/coverage.js +0 -62
- package/dist/_members/composition/reframe/derive.js +0 -73
- package/dist/_members/composition/reframe/index.js +0 -109
- package/dist/_members/composition/reframe/kenBurns.js +0 -64
- package/dist/_members/composition/reframe/shotScale.js +0 -176
- package/dist/_members/composition/reframe/trajectory.js +0 -124
- package/dist/_members/composition/timeline/compound.js +0 -134
- package/dist/_members/composition/timeline/index.js +0 -9
- package/dist/_members/composition/timeline/intent.js +0 -228
- package/dist/_members/composition/timeline/keyframes.js +0 -121
- package/dist/_members/composition/timeline/link.js +0 -59
- package/dist/_members/composition/timeline/occupancy.js +0 -36
- package/dist/_members/composition/timeline/placement.js +0 -74
- package/dist/_members/composition/timeline/plan.js +0 -31
- package/dist/_members/composition/timeline/resolve.js +0 -170
- package/dist/_members/composition/timeline/trims.js +0 -278
- package/dist/_members/composition/types/index.js +0 -1
- package/dist/_members/composition/validators/brandKit.js +0 -295
- package/dist/_members/composition/validators/composePlan.js +0 -113
- package/dist/_members/composition/validators/composition.js +0 -703
- package/dist/_members/composition/validators/font.js +0 -34
- package/dist/_members/composition/validators/graphicKinds.js +0 -30
- package/dist/_members/composition/validators/index.js +0 -9
- package/dist/_members/composition/validators/project.js +0 -29
- package/dist/_members/composition/validators/registry.js +0 -41
- package/dist/_members/composition/validators/render.js +0 -139
- package/dist/_members/composition/validators/session.js +0 -37
- package/dist/_members/composition/validators/webhook.js +0 -72
- package/dist/_members/composition-ops/alphaLayers.js +0 -141
- package/dist/_members/composition-ops/index.js +0 -637
- package/dist/_members/composition-ops/materializeGrid.js +0 -99
- package/dist/_members/composition-ops/opRecorder.js +0 -196
- package/dist/_members/composition-ops/recordingStore.js +0 -14
- package/dist/_members/composition-ops/replaceRegion.js +0 -102
- package/dist/_members/composition-ops/testFixtures.js +0 -19
- package/dist/_members/core/index.js +0 -5
- package/dist/_members/core/render/bookends.js +0 -53
- package/dist/_members/core/render/captions.js +0 -127
- package/dist/_members/core/render/compositionRender.js +0 -1249
- package/dist/_members/core/render/deriveTrajectory.js +0 -889
- package/dist/_members/core/render/emphasis.js +0 -16
- package/dist/_members/core/render/eval/cropBox.js +0 -139
- package/dist/_members/core/render/eval/inclusion.js +0 -96
- package/dist/_members/core/render/eval/index.js +0 -146
- package/dist/_members/core/render/eval/metrics.js +0 -94
- package/dist/_members/core/render/exportSettings.js +0 -65
- package/dist/_members/core/render/index.js +0 -20
- package/dist/_members/core/render/mapFaceToPerson.js +0 -73
- package/dist/_members/core/render/types.js +0 -8
- package/dist/_members/core/render/vcam/cameraPath.js +0 -248
- package/dist/_members/core/render/vcam/constants.js +0 -115
- package/dist/_members/core/render/vcam/errorCodes.js +0 -39
- package/dist/_members/core/render/vcam/framing.js +0 -136
- package/dist/_members/core/render/vcam/requiredRegion.js +0 -137
- package/dist/_members/core/render/vcam/shots.js +0 -53
- package/dist/_members/core/render/vcam/subjectSet.js +0 -388
- package/dist/_members/core/select/errorCodes.js +0 -38
- package/dist/_members/core/select/index.js +0 -26
- package/dist/_members/core/select/model.js +0 -18
- package/dist/_members/core/select/select.js +0 -244
- package/dist/_members/core/select/stabilize.js +0 -242
- package/dist/_members/core/types.js +0 -7
- package/dist/_members/errors/index.js +0 -1
- package/dist/_members/errors/registry.js +0 -560
- package/dist/_members/foundation/codes/index.js +0 -31
- package/dist/_members/foundation/index.js +0 -2
- package/dist/_members/foundation/primitives/index.js +0 -5
- package/dist/_members/mcp/adapter.js +0 -771
- package/dist/_members/mcp/burnedNames.js +0 -73
- package/dist/_members/mcp/core.js +0 -390
- package/dist/_members/mcp/curation.js +0 -792
- package/dist/_members/mcp/executor.js +0 -142
- package/dist/_members/mcp/format.js +0 -9
- package/dist/_members/mcp/generated/catalogPreviews.js +0 -95
- package/dist/_members/mcp/generated/harness.js +0 -81
- package/dist/_members/mcp/generated/operations.js +0 -131
- package/dist/_members/mcp/index.js +0 -50
- package/dist/_members/mcp/internalLoopTools.js +0 -109
- package/dist/_members/mcp/prompts.js +0 -45
- package/dist/_members/mcp/resources.js +0 -166
- package/dist/_members/mcp/server.js +0 -340
- package/dist/_members/mcp/wait.js +0 -378
- package/dist/_members/mcp/x402Meta.js +0 -66
- package/dist/_members/media-model/api.js +0 -175
- package/dist/_members/media-model/index.js +0 -1
- package/dist/_members/media-model/media-kind.js +0 -116
- package/dist/_members/media-model/testing/index.js +0 -1
- package/dist/_members/media-model/testing/mediaStoreContract.js +0 -82
- package/dist/_members/media-model/transcriptSearch.js +0 -70
- package/dist/_members/media-model/transcriptSearchCases.json +0 -79
- package/dist/_members/nle-export/compact-timeline.js +0 -73
- package/dist/_members/nle-export/compact-timeline.js.map +0 -1
- package/dist/_members/nle-export/composition-to-input.js +0 -147
- package/dist/_members/nle-export/composition-to-input.js.map +0 -1
- package/dist/_members/nle-export/fcpxml.js +0 -159
- package/dist/_members/nle-export/fcpxml.js.map +0 -1
- package/dist/_members/nle-export/index.js +0 -12
- package/dist/_members/nle-export/index.js.map +0 -1
- package/dist/_members/nle-export/premiere.js +0 -161
- package/dist/_members/nle-export/premiere.js.map +0 -1
- package/dist/_members/nle-export/server/index.js +0 -2
- package/dist/_members/nle-export/server/index.js.map +0 -1
- package/dist/_members/nle-export/server/pack-zip.js +0 -38
- package/dist/_members/nle-export/server/pack-zip.js.map +0 -1
- package/dist/_members/nle-export/srt.js +0 -50
- package/dist/_members/nle-export/srt.js.map +0 -1
- package/dist/_members/nle-export/types.js +0 -9
- package/dist/_members/nle-export/types.js.map +0 -1
- package/dist/_members/nle-export/xml/index.js +0 -11
- package/dist/_members/nle-export/xml/index.js.map +0 -1
- package/dist/_members/nle-export/xml-utils.js +0 -48
- package/dist/_members/nle-export/xml-utils.js.map +0 -1
- package/dist/_members/perception/activeSpeaker.js +0 -103
- package/dist/_members/perception/audioQuality.js +0 -76
- package/dist/_members/perception/energyBeat.js +0 -99
- package/dist/_members/perception/identityMatch.js +0 -91
- package/dist/_members/perception/index.js +0 -48
- package/dist/_members/perception/visualQuality.js +0 -95
- package/dist/_members/plane/conformance.js +0 -90
- package/dist/_members/plane/contract.js +0 -49
- package/dist/_members/plane/index.js +0 -16
- package/dist/_members/primitives/backgrounds/index.js +0 -32
- package/dist/_members/primitives/backgrounds/index.js.map +0 -1
- package/dist/_members/primitives/captions/index.js +0 -398
- package/dist/_members/primitives/captions/index.js.map +0 -1
- package/dist/_members/primitives/chunk-2LVE4UIT.js +0 -139
- package/dist/_members/primitives/chunk-2LVE4UIT.js.map +0 -1
- package/dist/_members/primitives/chunk-3IQUH6KK.js +0 -20
- package/dist/_members/primitives/chunk-3IQUH6KK.js.map +0 -1
- package/dist/_members/primitives/chunk-3OMURV6Q.js +0 -112
- package/dist/_members/primitives/chunk-3OMURV6Q.js.map +0 -1
- package/dist/_members/primitives/chunk-4IS6TMEG.js +0 -21
- package/dist/_members/primitives/chunk-4IS6TMEG.js.map +0 -1
- package/dist/_members/primitives/chunk-5MWGAEGQ.js +0 -1644
- package/dist/_members/primitives/chunk-5MWGAEGQ.js.map +0 -1
- package/dist/_members/primitives/chunk-7RRPFPQK.js +0 -15
- package/dist/_members/primitives/chunk-7RRPFPQK.js.map +0 -1
- package/dist/_members/primitives/chunk-DH5TRZK2.js +0 -125
- package/dist/_members/primitives/chunk-DH5TRZK2.js.map +0 -1
- package/dist/_members/primitives/chunk-E3KNUSFG.js +0 -2337
- package/dist/_members/primitives/chunk-E3KNUSFG.js.map +0 -1
- package/dist/_members/primitives/chunk-G2B3G4HK.js +0 -47
- package/dist/_members/primitives/chunk-G2B3G4HK.js.map +0 -1
- package/dist/_members/primitives/chunk-HFPFGZXT.js +0 -1494
- package/dist/_members/primitives/chunk-HFPFGZXT.js.map +0 -1
- package/dist/_members/primitives/chunk-HIWVFZVH.js +0 -19
- package/dist/_members/primitives/chunk-HIWVFZVH.js.map +0 -1
- package/dist/_members/primitives/chunk-IDAL6G3Y.js +0 -134
- package/dist/_members/primitives/chunk-IDAL6G3Y.js.map +0 -1
- package/dist/_members/primitives/chunk-KNC6JP5F.js +0 -34
- package/dist/_members/primitives/chunk-KNC6JP5F.js.map +0 -1
- package/dist/_members/primitives/chunk-KYRDVZST.js +0 -459
- package/dist/_members/primitives/chunk-KYRDVZST.js.map +0 -1
- package/dist/_members/primitives/chunk-LWLHS2NS.js +0 -3410
- package/dist/_members/primitives/chunk-LWLHS2NS.js.map +0 -1
- package/dist/_members/primitives/chunk-QAQ3P7DR.js +0 -8855
- package/dist/_members/primitives/chunk-QAQ3P7DR.js.map +0 -1
- package/dist/_members/primitives/chunk-ROV2ODWC.js +0 -81
- package/dist/_members/primitives/chunk-ROV2ODWC.js.map +0 -1
- package/dist/_members/primitives/chunk-TCRC2FH3.js +0 -183
- package/dist/_members/primitives/chunk-TCRC2FH3.js.map +0 -1
- package/dist/_members/primitives/chunk-US4YEDAA.js +0 -96
- package/dist/_members/primitives/chunk-US4YEDAA.js.map +0 -1
- package/dist/_members/primitives/chunk-UVKRO5ER.js +0 -13
- package/dist/_members/primitives/chunk-UVKRO5ER.js.map +0 -1
- package/dist/_members/primitives/chunk-W4EIIF2I.js +0 -10
- package/dist/_members/primitives/chunk-W4EIIF2I.js.map +0 -1
- package/dist/_members/primitives/chunk-WPGKIPCO.js +0 -3563
- package/dist/_members/primitives/chunk-WPGKIPCO.js.map +0 -1
- package/dist/_members/primitives/chunk-WZFLEGMD.js +0 -1435
- package/dist/_members/primitives/chunk-WZFLEGMD.js.map +0 -1
- package/dist/_members/primitives/chunk-XHU3WBEI.js +0 -33
- package/dist/_members/primitives/chunk-XHU3WBEI.js.map +0 -1
- package/dist/_members/primitives/chunk-YN7WTZZ7.js +0 -46
- package/dist/_members/primitives/chunk-YN7WTZZ7.js.map +0 -1
- package/dist/_members/primitives/effects/index.js +0 -30
- package/dist/_members/primitives/effects/index.js.map +0 -1
- package/dist/_members/primitives/hero-text/index.js +0 -14
- package/dist/_members/primitives/hero-text/index.js.map +0 -1
- package/dist/_members/primitives/index.js +0 -64
- package/dist/_members/primitives/index.js.map +0 -1
- package/dist/_members/primitives/layouts/index.js +0 -24
- package/dist/_members/primitives/layouts/index.js.map +0 -1
- package/dist/_members/primitives/lib/easings.js +0 -26
- package/dist/_members/primitives/lib/easings.js.map +0 -1
- package/dist/_members/primitives/lib/index.js +0 -74
- package/dist/_members/primitives/lib/index.js.map +0 -1
- package/dist/_members/primitives/lib/palette.js +0 -72
- package/dist/_members/primitives/lib/palette.js.map +0 -1
- package/dist/_members/primitives/lib/preview.js +0 -8
- package/dist/_members/primitives/lib/preview.js.map +0 -1
- package/dist/_members/primitives/lib/schema.js +0 -12
- package/dist/_members/primitives/lib/schema.js.map +0 -1
- package/dist/_members/primitives/lib/seeded-random.js +0 -14
- package/dist/_members/primitives/lib/seeded-random.js.map +0 -1
- package/dist/_members/primitives/lib/text/contrast.js +0 -12
- package/dist/_members/primitives/lib/text/contrast.js.map +0 -1
- package/dist/_members/primitives/lib/text/fill.js +0 -8
- package/dist/_members/primitives/lib/text/fill.js.map +0 -1
- package/dist/_members/primitives/lib/text/fitText.js +0 -10
- package/dist/_members/primitives/lib/text/fitText.js.map +0 -1
- package/dist/_members/primitives/lib/text/marker.js +0 -8
- package/dist/_members/primitives/lib/text/marker.js.map +0 -1
- package/dist/_members/primitives/lib/text/motion.js +0 -16
- package/dist/_members/primitives/lib/text/motion.js.map +0 -1
- package/dist/_members/primitives/lib/text/recolor.js +0 -8
- package/dist/_members/primitives/lib/text/recolor.js.map +0 -1
- package/dist/_members/primitives/lib/text/spec.js +0 -1
- package/dist/_members/primitives/lib/text/spec.js.map +0 -1
- package/dist/_members/primitives/lib/text/stage.js +0 -8
- package/dist/_members/primitives/lib/text/stage.js.map +0 -1
- package/dist/_members/primitives/lib/text/typeface.js +0 -8
- package/dist/_members/primitives/lib/text/typeface.js.map +0 -1
- package/dist/_members/primitives/lib/tokens.js +0 -24
- package/dist/_members/primitives/lib/tokens.js.map +0 -1
- package/dist/_members/primitives/lib/types.js +0 -1
- package/dist/_members/primitives/lib/types.js.map +0 -1
- package/dist/_members/primitives/params-required.json +0 -153
- package/dist/_members/primitives/scenes/index.js +0 -25
- package/dist/_members/primitives/scenes/index.js.map +0 -1
- package/dist/_members/primitives/subject-foreground/index.js +0 -30
- package/dist/_members/primitives/subject-foreground/index.js.map +0 -1
- package/dist/_members/primitives/text/index.js +0 -57
- package/dist/_members/primitives/text/index.js.map +0 -1
- package/dist/_members/primitives/transitions/index.js +0 -67
- package/dist/_members/primitives/transitions/index.js.map +0 -1
- package/dist/_members/primitives/ui-blocks/index.js +0 -76
- package/dist/_members/primitives/ui-blocks/index.js.map +0 -1
- package/dist/_members/primitives/version-map.json +0 -105
- package/dist/_members/primitives/workspace.js +0 -60
- package/dist/_members/primitives/workspace.js.map +0 -1
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { resolveBrandRefsDeep } from "../projection/brandKit.js";
|
|
2
|
-
// Brand-ref leaves (see brandKit ResolvedBrandKit). Fonts MUST use `.family`.
|
|
3
|
-
const B = {
|
|
4
|
-
textOnMedia: "$brand:colors.textOnMedia",
|
|
5
|
-
textOnAccent: "$brand:colors.textOnAccent",
|
|
6
|
-
textOnSurface: "$brand:colors.textOnSurface",
|
|
7
|
-
accent: "$brand:colors.accent",
|
|
8
|
-
overlay: "$brand:colors.overlay",
|
|
9
|
-
surface: "$brand:colors.surface",
|
|
10
|
-
fontHeading: "$brand:fonts.heading.family",
|
|
11
|
-
fontBody: "$brand:fonts.body.family",
|
|
12
|
-
motionEasing: "$brand:motion.easing",
|
|
13
|
-
motionEntrance: "$brand:motion.entrance",
|
|
14
|
-
};
|
|
15
|
-
function textColorRef(styleRole) {
|
|
16
|
-
return styleRole === "on-accent" ? B.textOnAccent : styleRole === "on-surface" ? B.textOnSurface : B.textOnMedia;
|
|
17
|
-
}
|
|
18
|
-
function fontRef(styleRole) {
|
|
19
|
-
return styleRole === "body" ? B.fontBody : B.fontHeading;
|
|
20
|
-
}
|
|
21
|
-
/** Font px from a box's height so the text FITS its box (leaves line room). */
|
|
22
|
-
function fontPx(boxH, frameH, fill = 0.62) {
|
|
23
|
-
return Math.round(Math.max(16, Math.min(240, boxH * frameH * fill)));
|
|
24
|
-
}
|
|
25
|
-
/** Bounding box of the front element boxes (clear of the face iff each is). */
|
|
26
|
-
function unionBox(rects) {
|
|
27
|
-
const x = Math.min(...rects.map((r) => r.x));
|
|
28
|
-
const y = Math.min(...rects.map((r) => r.y));
|
|
29
|
-
const x2 = Math.max(...rects.map((r) => r.x + r.w));
|
|
30
|
-
const y2 = Math.max(...rects.map((r) => r.y + r.h));
|
|
31
|
-
return { x, y, w: x2 - x, h: y2 - y };
|
|
32
|
-
}
|
|
33
|
-
/** Parse a stat string ("5 yrs", "100%", "$0 raised", "3,000 downloads") into
|
|
34
|
-
* stat-counter's numeric value + prefix/suffix. Non-digit prefix is greedy so a
|
|
35
|
-
* hyphen after letters ("Top-10") is NOT read as a negative; thousands commas
|
|
36
|
-
* are stripped from the number. null ⇒ no leading number. */
|
|
37
|
-
export function parseStatValue(raw) {
|
|
38
|
-
const m = raw.trim().match(/^(\D*)(\d[\d,]*(?:\.\d+)?)(.*)$/);
|
|
39
|
-
if (!m)
|
|
40
|
-
return null;
|
|
41
|
-
const value = Number(m[2].replace(/,/g, ""));
|
|
42
|
-
if (!Number.isFinite(value))
|
|
43
|
-
return null;
|
|
44
|
-
return { value, prefix: m[1].trim(), suffix: m[3] };
|
|
45
|
-
}
|
|
46
|
-
const asSource = (s) => s;
|
|
47
|
-
/** True iff two rects overlap. */
|
|
48
|
-
const overlaps = (a, b) => a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
|
|
49
|
-
/** Compile one beat (its content role + solved layout) into overlay clips. */
|
|
50
|
-
export function compileBeat(beat, layout, _motion, opts = {}) {
|
|
51
|
-
const prefix = opts.idPrefix ?? "gfx";
|
|
52
|
-
const frameH = layout.frame.h;
|
|
53
|
-
const kit = opts.brandKit;
|
|
54
|
-
// Resolve nested $brand: refs to literals at compile time when a kit is given
|
|
55
|
-
// (the render inflater only does top-level). Identity passthrough otherwise.
|
|
56
|
-
const finalize = (s) => kit ? { ...s, params: resolveBrandRefsDeep(s.params, kit) } : s;
|
|
57
|
-
const src = (s) => asSource(finalize(s));
|
|
58
|
-
// The behind-subject hero (heroText) — RE-ENABLED 2026-07-29. It was
|
|
59
|
-
// denylisted 2026-07-08 for "matte compositing desyncs lip movement"; the
|
|
60
|
-
// root cause was the matte bake re-timing the co-view at average fps (fixed
|
|
61
|
-
// by the CFR timing invariant — plan 2026-07-28-cfr-timing-invariant), not
|
|
62
|
-
// this emission. heroText is world-locked (zPlane, no authored anchor) so
|
|
63
|
-
// the silhouette gate places it — not the plan. Cross-beat duplicate
|
|
64
|
-
// protection (the OTHER disable reason: an agent plan authored two
|
|
65
|
-
// overlapping hero beats 0.14s apart) lives in compileGraphicsPlan.
|
|
66
|
-
// Text is REQUIRED: heroText's schema is min(1), and a Copy.headline of ""
|
|
67
|
-
// is schema-legal upstream (the solver omits `text` for falsy copy) — an
|
|
68
|
-
// unguarded emission would compile a clip the write boundary/render rejects
|
|
69
|
-
// for the whole plan. A textless hero drops out like other non-emitted roles.
|
|
70
|
-
const hero = layout.elements.find((e) => e.z === "behind-subject" && e.text?.trim());
|
|
71
|
-
// Only TEXT-emitting front roles composite here; logo/accent-shape/backdrop
|
|
72
|
-
// are non-text decoration (P1 skips them rather than emitting a blank title).
|
|
73
|
-
const front = layout.elements.filter((e) => e.z !== "behind-subject" && e.role !== "logo" && e.role !== "accent-shape" && e.role !== "backdrop");
|
|
74
|
-
const clips = [];
|
|
75
|
-
if (hero) {
|
|
76
|
-
clips.push({
|
|
77
|
-
id: `${prefix}-${beat.id}-hero`,
|
|
78
|
-
startTime: beat.atSec,
|
|
79
|
-
duration: beat.durSec,
|
|
80
|
-
kind: "overlay",
|
|
81
|
-
source: src({
|
|
82
|
-
kind: "overlay",
|
|
83
|
-
primitiveId: "heroText",
|
|
84
|
-
params: { text: hero.text, fontSizePct: 14, textColor: B.textOnMedia, fontFamily: B.fontHeading, entrance: B.motionEntrance, easing: B.motionEasing },
|
|
85
|
-
zPlane: "behind-subject",
|
|
86
|
-
}),
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
if (front.length === 0)
|
|
90
|
-
return clips;
|
|
91
|
-
const region = unionBox(front.map((e) => e.box));
|
|
92
|
-
// GUARD: the union of individually-clear boxes could still span the face (a
|
|
93
|
-
// split layout). If it does, skip the front graphic rather than composite it
|
|
94
|
-
// OVER the speaker — the invariant holds even when the solver doesn't stack
|
|
95
|
-
// contiguously (e.g. a future agent-proposed layout).
|
|
96
|
-
if (layout.keepOut.some((ko) => overlaps(region, ko)))
|
|
97
|
-
return clips;
|
|
98
|
-
// A text graphic MUST have a primary line (headline/value). No fallback to
|
|
99
|
-
// front[0] — a beat with only a subtitle is malformed; fail loud.
|
|
100
|
-
const head = front.find((e) => e.role === "headline" || e.role === "value");
|
|
101
|
-
if (!head) {
|
|
102
|
-
throw new Error(`graphics-plan: beat "${beat.id}" (role ${beat.role}) has front elements but no headline/value line — malformed copy.`);
|
|
103
|
-
}
|
|
104
|
-
// sub must be a DISTINCT element from head (else a subtitle-only beat would
|
|
105
|
-
// render the same string twice).
|
|
106
|
-
const sub = front.find((e) => (e.role === "sublabel" || e.role === "label") && e !== head);
|
|
107
|
-
const headFont = fontPx(head.box.h, frameH);
|
|
108
|
-
// Subtitle never larger than the headline (hierarchy), regardless of box math.
|
|
109
|
-
const subFont = sub ? Math.min(fontPx(sub.box.h, frameH), Math.round(headFont * 0.8)) : undefined;
|
|
110
|
-
const base = { id: `${prefix}-${beat.id}`, startTime: beat.atSec, duration: beat.durSec, kind: "overlay", region };
|
|
111
|
-
const titleSource = (styleRole) => ({
|
|
112
|
-
kind: "overlay",
|
|
113
|
-
primitiveId: "cinematic-title",
|
|
114
|
-
params: {
|
|
115
|
-
text: head.text ?? "",
|
|
116
|
-
...(sub?.text ? { subtitle: sub.text } : {}),
|
|
117
|
-
tokens: { fontSize: headFont, ...(subFont ? { subtextFontSize: subFont } : {}), textColor: textColorRef(styleRole), accentColor: B.accent, fontFamily: fontRef(styleRole) },
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
switch (beat.role) {
|
|
121
|
-
case "title-card":
|
|
122
|
-
clips.push({ ...base, source: src(titleSource(head.styleRole)) });
|
|
123
|
-
break;
|
|
124
|
-
case "lower-third":
|
|
125
|
-
clips.push({
|
|
126
|
-
...base,
|
|
127
|
-
source: src({
|
|
128
|
-
kind: "overlay",
|
|
129
|
-
primitiveId: "lower-third",
|
|
130
|
-
params: {
|
|
131
|
-
text: head.text ?? "",
|
|
132
|
-
...(sub?.text ? { subtitle: sub.text } : {}),
|
|
133
|
-
tokens: { fontSize: headFont, ...(subFont ? { subtitleFontSize: subFont } : {}), textColor: B.textOnMedia, accentColor: B.accent, backgroundColor: B.overlay, fontFamily: B.fontBody },
|
|
134
|
-
},
|
|
135
|
-
}),
|
|
136
|
-
});
|
|
137
|
-
break;
|
|
138
|
-
case "stat": {
|
|
139
|
-
const parsed = head.text ? parseStatValue(head.text) : null;
|
|
140
|
-
if (parsed) {
|
|
141
|
-
clips.push({
|
|
142
|
-
...base,
|
|
143
|
-
source: src({
|
|
144
|
-
kind: "overlay",
|
|
145
|
-
primitiveId: "stat-counter",
|
|
146
|
-
params: { value: parsed.value, ...(parsed.prefix ? { prefix: parsed.prefix } : {}), ...(parsed.suffix ? { suffix: parsed.suffix } : {}), ...(sub?.text ? { label: sub.text } : {}), accentColor: B.accent },
|
|
147
|
-
}),
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
// A stat REQUIRES a leading number — no silent degrade to a title card.
|
|
152
|
-
throw new Error(`graphics-plan: beat "${beat.id}" role "stat" has non-numeric value "${head.text}" — a stat needs a leading number (e.g. "5 yrs", "100%", "498").`);
|
|
153
|
-
}
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
case "pull-quote":
|
|
157
|
-
clips.push({ ...base, source: src({ kind: "overlay", primitiveId: "quote-card", params: { text: head.text ?? "", ...(sub?.text ? { subtext: sub.text } : {}), accentColor: B.accent } }) });
|
|
158
|
-
break;
|
|
159
|
-
case "enumeration":
|
|
160
|
-
case "sequence":
|
|
161
|
-
case "comparison":
|
|
162
|
-
// The ARGUMENT roles are DESIGNER-AUTHORED, by design. There is no deterministic
|
|
163
|
-
// role→primitive mapping for them and there should not be: an `enumeration` might be
|
|
164
|
-
// three badges, a numbered list or a build-on-reveal, and a `sequence` might be
|
|
165
|
-
// `reasoning-trace`, `tool-call-stack` or a bespoke component — that choice is the
|
|
166
|
-
// designer's job, made with eyes on the frame and the whole catalog in hand.
|
|
167
|
-
//
|
|
168
|
-
// This deterministic FC5 compiler was DEMOTED to critics in any case (director.ts: "they
|
|
169
|
-
// no longer COMPILE the design") and `compilePlanGraphics` has no live callers. It is kept
|
|
170
|
-
// for an optional deterministic seed. Failing loud HERE — rather than silently rendering a
|
|
171
|
-
// title card — is the honest behaviour if it is ever re-promoted.
|
|
172
|
-
throw new Error(`graphics-plan: beat "${beat.id}" role "${beat.role}" is a DESIGNER-AUTHORED argument role ` +
|
|
173
|
-
`(enumeration / sequence / comparison). It has no deterministic primitive mapping by ` +
|
|
174
|
-
`design — the designer picks the component from the catalog or writes one. This ` +
|
|
175
|
-
`compiler only handles the text-shaped roles.`);
|
|
176
|
-
default:
|
|
177
|
-
// hero/logo-tag never reach here (no front text elements); any role that
|
|
178
|
-
// does is unhandled — fail loud instead of silently rendering a title.
|
|
179
|
-
throw new Error(`graphics-plan: beat "${beat.id}" role "${beat.role}" reached the compiler with front text but no handler.`);
|
|
180
|
-
}
|
|
181
|
-
return clips;
|
|
182
|
-
}
|
|
183
|
-
/** Compile a whole GraphicsPlan (all beats) into overlay clips. */
|
|
184
|
-
export function compileGraphicsPlan(plan, opts = {}) {
|
|
185
|
-
const motionByBeat = new Map(plan.motions.map((m) => [m.beatId, m]));
|
|
186
|
-
const layoutByBeat = new Map(plan.layouts.map((l) => [l.beatId, l]));
|
|
187
|
-
const clips = [];
|
|
188
|
-
for (const beat of plan.content.beats) {
|
|
189
|
-
const layout = layoutByBeat.get(beat.id);
|
|
190
|
-
if (!layout)
|
|
191
|
-
continue;
|
|
192
|
-
clips.push(...compileBeat(beat, layout, motionByBeat.get(beat.id), opts));
|
|
193
|
-
}
|
|
194
|
-
return dedupeOverlappingHeroes(clips);
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* MECHANICAL guard for the hero param-snag that co-motivated the 2026-07-08
|
|
198
|
-
* disable: the 2026-07-07 prod e2e produced TWO overlapping heroText beats
|
|
199
|
-
* (7.93s & 8.07s) — stacked display phrases rendering over each other. One
|
|
200
|
-
* hero at a time is the grammar ("ration it hard: ONE hero beat per clip"),
|
|
201
|
-
* so enforce it here rather than trusting the planner prompt: when two
|
|
202
|
-
* compiled heroText windows overlap in time, the EARLIER one wins and the
|
|
203
|
-
* later duplicate is dropped. Non-overlapping heroes (distinct moments in a
|
|
204
|
-
* long film) all survive.
|
|
205
|
-
*/
|
|
206
|
-
function dedupeOverlappingHeroes(clips) {
|
|
207
|
-
const isHero = (c) => c.source?.primitiveId === "heroText";
|
|
208
|
-
const heroes = clips
|
|
209
|
-
.filter(isHero)
|
|
210
|
-
.sort((a, b) => a.startTime - b.startTime || a.id.localeCompare(b.id));
|
|
211
|
-
const dropped = new Set();
|
|
212
|
-
let lastEnd = -Infinity;
|
|
213
|
-
for (const h of heroes) {
|
|
214
|
-
if (h.startTime < lastEnd) {
|
|
215
|
-
dropped.add(h.id);
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
lastEnd = h.startTime + h.duration;
|
|
219
|
-
}
|
|
220
|
-
return dropped.size === 0 ? clips : clips.filter((c) => !dropped.has(c.id));
|
|
221
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Graphics-plan IR — public surface. See ./schemas.ts for the contract and
|
|
3
|
-
* brain/Projects/CueFrame/plans/2026-07-07-structure-first-graphics-pipeline.md
|
|
4
|
-
* for the design.
|
|
5
|
-
*/
|
|
6
|
-
export * from "./schemas.js";
|
|
7
|
-
export * from "./solve.js";
|
|
8
|
-
export * from "./motion.js";
|
|
9
|
-
export * from "./compile.js";
|
|
10
|
-
export { instantiateMotif, MotifInstantiationError } from "./instantiateMotif.js";
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic instantiation of a REPRODUCED brand-kit motif skin — Lane A of
|
|
3
|
-
* the two-lane graphics agent (plan 2026-07-16). A skin is the motif's verified
|
|
4
|
-
* authored form (`brandMotif.body`, pixel-diffed against the reference at
|
|
5
|
-
* ingestion); at compose time the graphic's copy is slotted into it with NO
|
|
6
|
-
* LLM: pure text replacement under the slot convention.
|
|
7
|
-
*
|
|
8
|
-
* SLOT CONVENTION (defined here, enforced here, authored by the reproduction
|
|
9
|
-
* loop): copy-bearing elements carry `data-slot="<copyKey>"` and contain ONLY
|
|
10
|
-
* text. That constraint is what makes regex replacement sound — no HTML parser
|
|
11
|
-
* dependency enters the composition leaf. Every violation FAILS LOUD; a silent
|
|
12
|
-
* redesign is exactly the variance this lane exists to remove.
|
|
13
|
-
*/
|
|
14
|
-
export class MotifInstantiationError extends Error {
|
|
15
|
-
}
|
|
16
|
-
const SLOT_RE = /(<[^>]*\bdata-slot="([^"]+)"[^>]*>)([\s\S]*?)(<\/)/g;
|
|
17
|
-
const escapeHtml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
18
|
-
/** Fill a reproduced card skin's slots with the graphic's copy. Throws
|
|
19
|
-
* MotifInstantiationError on any deviation from the convention. */
|
|
20
|
-
export function instantiateMotif(motif, copy) {
|
|
21
|
-
if (motif.coverage !== "reproduced") {
|
|
22
|
-
throw new MotifInstantiationError(`motif coverage is '${motif.coverage}' — only 'reproduced' skins instantiate; approximated/skipped motifs go to the designer`);
|
|
23
|
-
}
|
|
24
|
-
if (motif.body.kind !== "card") {
|
|
25
|
-
throw new MotifInstantiationError(`v1 instantiates card bodies only (got '${motif.body.kind}')`);
|
|
26
|
-
}
|
|
27
|
-
const seen = new Set();
|
|
28
|
-
const html = motif.body.html.replace(SLOT_RE, (_m, open, slot, inner, close) => {
|
|
29
|
-
if (/[<>]/.test(inner)) {
|
|
30
|
-
throw new MotifInstantiationError(`slot '${slot}' contains nested markup — skins must keep slots text-only`);
|
|
31
|
-
}
|
|
32
|
-
if (!(slot in copy)) {
|
|
33
|
-
throw new MotifInstantiationError(`slot '${slot}' not filled — copy carries [${Object.keys(copy).join(", ")}]`);
|
|
34
|
-
}
|
|
35
|
-
seen.add(slot);
|
|
36
|
-
return `${open}${escapeHtml(copy[slot])}${close}`;
|
|
37
|
-
});
|
|
38
|
-
for (const key of Object.keys(copy)) {
|
|
39
|
-
if (!seen.has(key)) {
|
|
40
|
-
throw new MotifInstantiationError(`copy has no slot '${key}' in the skin`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return { html, ...(motif.body.tokens ? { tokens: motif.body.tokens } : {}) };
|
|
44
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Motion stage: LayoutPlan → MotionPlan. Assigns a sensible default
|
|
3
|
-
* entrance/emphasis/exit per element role, staggered so elements don't all
|
|
4
|
-
* arrive at once. This is the inspectable MOTION artifact of the plan (a
|
|
5
|
-
* checkpoint surface, and what an agent revises).
|
|
6
|
-
*
|
|
7
|
-
* NOTE (honest status): the compiler does NOT yet consume MotionPlan — the
|
|
8
|
-
* primitives currently animate from their own `$brand:motion` defaults
|
|
9
|
-
* (entrance/duration/easing). Wiring MotionPlan → per-primitive animation
|
|
10
|
-
* params is a follow-up (FC3). Until then this plan is descriptive, not
|
|
11
|
-
* yet the driver.
|
|
12
|
-
*/
|
|
13
|
-
import { GRAPHICS_PLAN_VERSION, } from "./schemas.js";
|
|
14
|
-
/** Per-role entrance intent. Values (the number to remember) count up; supporting
|
|
15
|
-
* text rises; the primary headline reveals; decoration fades. */
|
|
16
|
-
const ENTER = {
|
|
17
|
-
headline: { type: "reveal", atMs: 0, durMs: 600, ease: "spring" },
|
|
18
|
-
value: { type: "count-up", atMs: 0, durMs: 800, ease: "spring" },
|
|
19
|
-
label: { type: "rise", atMs: 0, durMs: 500, ease: "ease-out" },
|
|
20
|
-
sublabel: { type: "fade", atMs: 0, durMs: 500, ease: "ease-out" },
|
|
21
|
-
logo: { type: "fade", atMs: 0, durMs: 400, ease: "ease-out" },
|
|
22
|
-
"accent-shape": { type: "wipe", atMs: 0, durMs: 500, ease: "ease-out" },
|
|
23
|
-
backdrop: { type: "fade", atMs: 0, durMs: 300, ease: "linear" },
|
|
24
|
-
};
|
|
25
|
-
/** Per-element entrance stagger (ms), applied in element order. */
|
|
26
|
-
const STAGGER_MS = 120;
|
|
27
|
-
/**
|
|
28
|
-
* Build the motion plan for a laid-out beat. Entrances stagger by element order;
|
|
29
|
-
* everything fades out together in the last ~350ms of the beat so a graphic never
|
|
30
|
-
* hard-cuts. `durSec` is the beat length (from the ContentPlan beat).
|
|
31
|
-
*/
|
|
32
|
-
export function planMotion(layout, durSec) {
|
|
33
|
-
const beatMs = Math.max(0, durSec * 1000);
|
|
34
|
-
const exitDur = Math.min(350, beatMs * 0.2);
|
|
35
|
-
const exitAt = Math.max(0, beatMs - exitDur);
|
|
36
|
-
const n = layout.elements.length;
|
|
37
|
-
// Fit ALL entrances within [0, exitAt]: an entrance must finish before the
|
|
38
|
-
// fade starts (on a short beat the nominal 800ms count-up + stagger would
|
|
39
|
-
// otherwise run past the exit or start after the beat ends). Scale the
|
|
40
|
-
// nominal durations + stagger by whatever factor makes the last entrance land
|
|
41
|
-
// by exitAt. `scale === 0` (zero-length beat) yields instantaneous entrances.
|
|
42
|
-
let nominalMaxEnd = 0;
|
|
43
|
-
layout.elements.forEach((el, i) => {
|
|
44
|
-
nominalMaxEnd = Math.max(nominalMaxEnd, i * STAGGER_MS + ENTER[el.role].durMs);
|
|
45
|
-
});
|
|
46
|
-
const scale = nominalMaxEnd > 0 && nominalMaxEnd > exitAt ? exitAt / nominalMaxEnd : 1;
|
|
47
|
-
const elements = layout.elements.map((el, i) => {
|
|
48
|
-
const base = ENTER[el.role];
|
|
49
|
-
const atMs = Math.round(i * STAGGER_MS * scale);
|
|
50
|
-
const durMs = Math.max(1, Math.round(base.durMs * scale));
|
|
51
|
-
const enter = { ...base, atMs, durMs };
|
|
52
|
-
const me = { elId: el.elId, enter };
|
|
53
|
-
// Emphasis pulse only if it fully FITS between the entrance and the exit
|
|
54
|
-
// fade (start AND end before exitAt — a pulse overrunning the fade jitters).
|
|
55
|
-
if (n > 0 && (el.role === "value" || el.role === "headline")) {
|
|
56
|
-
const emphAt = atMs + durMs + Math.round(150 * scale);
|
|
57
|
-
const emphDur = Math.round(300 * scale);
|
|
58
|
-
if (emphDur > 0 && emphAt + emphDur <= exitAt)
|
|
59
|
-
me.emphasis = { type: "pulse", atMs: emphAt, durMs: emphDur };
|
|
60
|
-
}
|
|
61
|
-
if (exitDur > 0)
|
|
62
|
-
me.exit = { type: "fade", atMs: exitAt, durMs: exitDur };
|
|
63
|
-
return me;
|
|
64
|
-
});
|
|
65
|
-
return { version: GRAPHICS_PLAN_VERSION, beatId: layout.beatId, elements };
|
|
66
|
-
}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Graphics-plan IR — the structure-first authoring contract (plan
|
|
3
|
-
* 2026-07-07-structure-first-graphics-pipeline). Instead of the designer
|
|
4
|
-
* authoring Remotion JSX blind, it produces these three inspectable,
|
|
5
|
-
* versioned artifacts; a compiler turns them into brand-bound, animatable
|
|
6
|
-
* Remotion. The SAME schema is the client-agent CHECKPOINT payload AND the
|
|
7
|
-
* compiler input — no translation layer to drift.
|
|
8
|
-
*
|
|
9
|
-
* Split by concern so each stage boundary is a review/edit point:
|
|
10
|
-
* ContentPlan — WHAT to say (editorial, text-owned; never pixels).
|
|
11
|
-
* LayoutPlan — WHERE it sits (spatial structure, content-aware: element
|
|
12
|
-
* boxes are solved CLEAR of the face keep-outs the
|
|
13
|
-
* composition-floor already computes → text-over-face is
|
|
14
|
-
* impossible by construction, not by prompt).
|
|
15
|
-
* MotionPlan — HOW it animates (entrance / emphasis / exit intent).
|
|
16
|
-
*
|
|
17
|
-
* `styleRole` binds to brand tokens (never a raw hex/font) so off-brand
|
|
18
|
-
* output is impossible; the compiler resolves it via the same `$brand:`
|
|
19
|
-
* mechanism as packages/authoring/src/tools/brandTokens.ts.
|
|
20
|
-
*/
|
|
21
|
-
import { z } from "zod";
|
|
22
|
-
import { Region } from "../validators/composition.js";
|
|
23
|
-
/** Bump when a field's meaning changes; the compiler + checkpoint clients
|
|
24
|
-
* branch on it. Kept a literal so an old client sees a hard mismatch, not a
|
|
25
|
-
* silent misread. */
|
|
26
|
-
export const GRAPHICS_PLAN_VERSION = "gp-1";
|
|
27
|
-
const Version = z.literal(GRAPHICS_PLAN_VERSION);
|
|
28
|
-
/** A rect in output-frame fractions [0,1] — the SAME placement vocabulary as
|
|
29
|
-
* the composition-floor `Rect` and the wire `Region` (reused so a plan box and
|
|
30
|
-
* a rendered layer can never disagree about coordinates). */
|
|
31
|
-
export const PlanRect = Region;
|
|
32
|
-
// ── ContentPlan ────────────────────────────────────────────────────────────
|
|
33
|
-
/**
|
|
34
|
-
* What the beat must COMMUNICATE. An EDITORIAL vocabulary — deliberately not a list of
|
|
35
|
-
* primitive names.
|
|
36
|
-
*
|
|
37
|
-
* The first six are named after primitive families, and that was the bug: the DESIGNER's
|
|
38
|
-
* decision (which component draws this) had leaked into the PLANNER's schema. It meant the
|
|
39
|
-
* vocabulary could only grow by adding primitive names, so it could never keep up with the
|
|
40
|
-
* catalog — and on 2026-07-13 a brief demanding a chain-trace, comparison bars and a
|
|
41
|
-
* three-item build got exactly ONE lower-third, because a lower-third was close to the most
|
|
42
|
-
* the planner was ABLE to say. It was not being lazy. It had no word for what the brief wanted.
|
|
43
|
-
*
|
|
44
|
-
* The last three are the fix, and they name a JOB, not a picture:
|
|
45
|
-
* the planner says "this beat must show a sequence"; the DESIGNER decides whether that is
|
|
46
|
-
* `reasoning-trace`, `tool-call-stack`, or something it writes itself.
|
|
47
|
-
*
|
|
48
|
-
* Closed on purpose (the critics and the layout solver switch on it) — but closed around
|
|
49
|
-
* editorial intent, which is a small and stable set, instead of around a 103-component catalog,
|
|
50
|
-
* which is neither.
|
|
51
|
-
*/
|
|
52
|
-
export const GraphicRole = z.enum([
|
|
53
|
-
"title-card", // topic-setter / opener card
|
|
54
|
-
"stat", // a single headline number + label
|
|
55
|
-
"lower-third", // name/title tag over the speaker
|
|
56
|
-
"pull-quote", // a short quoted line
|
|
57
|
-
"hero", // big behind-subject phrase (zPlane: behind-subject)
|
|
58
|
-
"logo-tag", // small brand mark / watermark beat
|
|
59
|
-
// ── the beat carries an ARGUMENT, not a caption ──
|
|
60
|
-
"enumeration", // a list of short items ("LBO modelling / VDR diligence / customer cubes")
|
|
61
|
-
"sequence", // an ordered chain of steps — often the POINT is that it is too long
|
|
62
|
-
"comparison", // labelled values set against each other (a benchmark, a before/after)
|
|
63
|
-
]);
|
|
64
|
-
/**
|
|
65
|
-
* The words for a beat — ALWAYS plan-owned (a text-to-image model can't spell; the Remotion code
|
|
66
|
-
* renders these crisply). All optional; a role uses the subset it needs
|
|
67
|
-
* (stat → value+label; title-card → headline+sub; enumeration/sequence → items; comparison → series).
|
|
68
|
-
*
|
|
69
|
-
* `items` and `series` exist because the plan OWNS the words, and a role it cannot supply the
|
|
70
|
-
* words for is a role it cannot actually request. Four scalars could not carry a list or a
|
|
71
|
-
* labelled series, so even a widened role vocabulary would have had nowhere to put its content —
|
|
72
|
-
* the second half of the same 2026-07-13 bug, and the one that would have silently defeated the
|
|
73
|
-
* fix for the first half.
|
|
74
|
-
*/
|
|
75
|
-
export const Copy = z
|
|
76
|
-
.object({
|
|
77
|
-
headline: z.string().max(120).optional(),
|
|
78
|
-
sub: z.string().max(200).optional(),
|
|
79
|
-
value: z.string().max(40).optional(),
|
|
80
|
-
label: z.string().max(80).optional(),
|
|
81
|
-
/** Ordered short items — an `enumeration`'s bullets, a `sequence`'s steps. */
|
|
82
|
-
items: z.array(z.string().max(80)).max(8).optional(),
|
|
83
|
-
/**
|
|
84
|
-
* Labelled values for a `comparison`.
|
|
85
|
-
*
|
|
86
|
-
* `value` is a STRING, deliberately — not `number | string`. The plan owns the words, and a
|
|
87
|
-
* comparison's words include their units: a planner writes "10 min", "98%", "$4.2M", never
|
|
88
|
-
* 600. A number would have to be re-unitised downstream by someone who doesn't know what it
|
|
89
|
-
* meant.
|
|
90
|
-
*
|
|
91
|
-
* It is also a hard constraint: `submit_compose_plan` is a STRICT Anthropic tool
|
|
92
|
-
* (`strict: true`, schemas.ts), and a `z.union` here becomes an `anyOf` in the tool schema.
|
|
93
|
-
* That is what stalled the planner on 2026-07-13 — it ran, could not satisfy the schema, and
|
|
94
|
-
* never called the tool at all. Keep every field in this schema a single concrete type.
|
|
95
|
-
*/
|
|
96
|
-
series: z
|
|
97
|
-
.array(z.object({ label: z.string().max(40), value: z.string().max(16) }))
|
|
98
|
-
.max(8)
|
|
99
|
-
.optional(),
|
|
100
|
-
})
|
|
101
|
-
.strict();
|
|
102
|
-
export const ContentBeat = z
|
|
103
|
-
.object({
|
|
104
|
-
id: z.string().min(1),
|
|
105
|
-
/** Clip-relative seconds. */
|
|
106
|
-
atSec: z.number().min(0),
|
|
107
|
-
durSec: z.number().positive(),
|
|
108
|
-
role: GraphicRole,
|
|
109
|
-
copy: Copy,
|
|
110
|
-
/** Editorial WHY — the beat this graphic serves (for review + the judge). */
|
|
111
|
-
intent: z.string().min(1).max(400),
|
|
112
|
-
/** Higher = more important; drives drop order under budget. */
|
|
113
|
-
priority: z.number().int().min(0).max(10).default(5),
|
|
114
|
-
})
|
|
115
|
-
.strict();
|
|
116
|
-
export const ContentPlan = z
|
|
117
|
-
.object({
|
|
118
|
-
version: Version,
|
|
119
|
-
beats: z.array(ContentBeat),
|
|
120
|
-
})
|
|
121
|
-
.strict();
|
|
122
|
-
// ── LayoutPlan ───────────────────────────────────────────────────────────────
|
|
123
|
-
/** The role of ONE element within a graphic — maps to a primitive slot. */
|
|
124
|
-
export const ElementRole = z.enum([
|
|
125
|
-
"headline", // primary large text
|
|
126
|
-
"value", // the stat number / the one word to remember
|
|
127
|
-
"label", // supporting text
|
|
128
|
-
"sublabel", // secondary supporting text
|
|
129
|
-
"logo", // brand mark
|
|
130
|
-
"accent-shape", // decorative block/line/underline
|
|
131
|
-
"backdrop", // solid fill (takeover cards only)
|
|
132
|
-
]);
|
|
133
|
-
/** Semantic style binding → resolved to `$brand:` refs by the compiler; NEVER a
|
|
134
|
-
* raw hex/font. This is the guarantee that on-brand is the only option.
|
|
135
|
-
* Mapping (compiler): heading→fonts.heading+colors.text, accent→colors.accent,
|
|
136
|
-
* body→fonts.body+colors.text, surface→colors.surface/background,
|
|
137
|
-
* on-accent→colors.textOnAccent, on-surface→colors.textOnSurface. */
|
|
138
|
-
export const StyleRole = z.enum([
|
|
139
|
-
"heading",
|
|
140
|
-
"body",
|
|
141
|
-
"accent",
|
|
142
|
-
"surface",
|
|
143
|
-
"on-accent",
|
|
144
|
-
"on-surface",
|
|
145
|
-
]);
|
|
146
|
-
/** Depth plane — matches OverlayClipSource.zPlane. `behind-subject` is the
|
|
147
|
-
* intentional hero look (the subject cut-out bites the letters). */
|
|
148
|
-
export const ZPlane = z.enum(["front", "behind-subject"]);
|
|
149
|
-
export const LayoutElement = z
|
|
150
|
-
.object({
|
|
151
|
-
elId: z.string().min(1),
|
|
152
|
-
role: ElementRole,
|
|
153
|
-
/** Solved CLEAR of `keepOut` (except intentional behind-subject heroes). */
|
|
154
|
-
box: PlanRect,
|
|
155
|
-
styleRole: StyleRole,
|
|
156
|
-
z: ZPlane.default("front"),
|
|
157
|
-
/** The text this element renders (from the ContentPlan). Omitted for
|
|
158
|
-
* logo / accent-shape / backdrop. */
|
|
159
|
-
text: z.string().max(200).optional(),
|
|
160
|
-
/** Index into the editorial type scale (mirrors composition-floor
|
|
161
|
-
* Treatment.typeScaleStep) — a SCALE STEP, not a raw px guess. */
|
|
162
|
-
typeScaleStep: z.number().int().min(0).max(8).optional(),
|
|
163
|
-
})
|
|
164
|
-
.strict();
|
|
165
|
-
const EPS = 1e-6;
|
|
166
|
-
const rectsOverlapEps = (a, b) => a.x < b.x + b.w - EPS && a.x + a.w > b.x + EPS && a.y < b.y + b.h - EPS && a.y + a.h > b.y + EPS;
|
|
167
|
-
export const LayoutPlan = z
|
|
168
|
-
.object({
|
|
169
|
-
version: Version,
|
|
170
|
-
beatId: z.string().min(1),
|
|
171
|
-
/** Output aspect context (pixels); placement itself is in [0,1] fractions. */
|
|
172
|
-
frame: z.object({ w: z.number().positive(), h: z.number().positive() }).strict(),
|
|
173
|
-
/** INPUT (not authored): face keep-out rects at this beat, from the
|
|
174
|
-
* composition-floor. The layout stage MUST place `front` elements clear of
|
|
175
|
-
* these — carried on the plan so a reviewer/compiler can verify it. */
|
|
176
|
-
keepOut: z.array(PlanRect),
|
|
177
|
-
/** INPUT: safe-zone insets (title-safe / action-safe fractions). */
|
|
178
|
-
safe: z
|
|
179
|
-
.object({
|
|
180
|
-
titleSafe: z.number().min(0).max(1),
|
|
181
|
-
actionSafe: z.number().min(0).max(1),
|
|
182
|
-
})
|
|
183
|
-
.strict(),
|
|
184
|
-
elements: z.array(LayoutElement),
|
|
185
|
-
/** Set when the solver could NOT place a beat's graphic legally (e.g. the
|
|
186
|
-
* face fills the legal area). Makes a drop OBSERVABLE — distinct from a
|
|
187
|
-
* beat that legitimately had no copy (both yield elements:[]). The producer
|
|
188
|
-
* can surface it; the compiler skips it. */
|
|
189
|
-
dropped: z.object({ reason: z.string().min(1) }).strict().optional(),
|
|
190
|
-
})
|
|
191
|
-
.strict()
|
|
192
|
-
// The IR ENFORCES its own headline invariant: a `front` element box must be
|
|
193
|
-
// in-frame AND clear of every face keep-out. A mis-solved OR (P4) agent-
|
|
194
|
-
// proposed plan that puts text on the speaker FAILS parse — the guarantee is
|
|
195
|
-
// structural, not just solver-code-dependent. behind-subject heroes are
|
|
196
|
-
// exempt (the subject intentionally occludes them).
|
|
197
|
-
.superRefine((plan, ctx) => {
|
|
198
|
-
for (const el of plan.elements) {
|
|
199
|
-
if (el.z === "behind-subject")
|
|
200
|
-
continue;
|
|
201
|
-
const b = el.box;
|
|
202
|
-
if (b.x + b.w > 1 + EPS || b.y + b.h > 1 + EPS) {
|
|
203
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `element ${el.elId} box spills past the frame`, path: ["elements"] });
|
|
204
|
-
}
|
|
205
|
-
for (const ko of plan.keepOut) {
|
|
206
|
-
if (rectsOverlapEps(b, ko)) {
|
|
207
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `element ${el.elId} overlaps a face keep-out (text on the speaker)`, path: ["elements"] });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
// ── MotionPlan ───────────────────────────────────────────────────────────────
|
|
213
|
-
export const EnterType = z.enum(["reveal", "fade", "rise", "count-up", "wipe"]);
|
|
214
|
-
export const EmphasisType = z.enum(["pulse", "underline", "color-shift"]);
|
|
215
|
-
export const ExitType = z.enum(["fade", "fall", "wipe", "cut"]);
|
|
216
|
-
/** Ease vocabulary → compiler maps to real spring/interpolate. Named, not raw
|
|
217
|
-
* bezier/ms, so the brand's motion pace stays a decision not a magic number. */
|
|
218
|
-
export const Ease = z.enum(["spring", "ease-out", "ease-in-out", "linear"]);
|
|
219
|
-
export const EnterMotion = z
|
|
220
|
-
.object({
|
|
221
|
-
type: EnterType,
|
|
222
|
-
atMs: z.number().min(0),
|
|
223
|
-
durMs: z.number().positive(),
|
|
224
|
-
ease: Ease.default("spring"),
|
|
225
|
-
/** Per-child stagger (ms) for multi-part reveals. */
|
|
226
|
-
stagger: z.number().min(0).optional(),
|
|
227
|
-
})
|
|
228
|
-
.strict();
|
|
229
|
-
export const EmphasisMotion = z
|
|
230
|
-
.object({ type: EmphasisType, atMs: z.number().min(0), durMs: z.number().positive() })
|
|
231
|
-
.strict();
|
|
232
|
-
export const ExitMotion = z
|
|
233
|
-
.object({ type: ExitType, atMs: z.number().min(0), durMs: z.number().positive() })
|
|
234
|
-
.strict();
|
|
235
|
-
export const MotionElement = z
|
|
236
|
-
.object({
|
|
237
|
-
elId: z.string().min(1),
|
|
238
|
-
enter: EnterMotion,
|
|
239
|
-
emphasis: EmphasisMotion.optional(),
|
|
240
|
-
exit: ExitMotion.optional(),
|
|
241
|
-
})
|
|
242
|
-
.strict();
|
|
243
|
-
export const MotionPlan = z
|
|
244
|
-
.object({
|
|
245
|
-
version: Version,
|
|
246
|
-
beatId: z.string().min(1),
|
|
247
|
-
elements: z.array(MotionElement),
|
|
248
|
-
})
|
|
249
|
-
.strict();
|
|
250
|
-
// ── Bundle ───────────────────────────────────────────────────────────────────
|
|
251
|
-
/** The full graphics plan for a compose run: one ContentPlan, and a
|
|
252
|
-
* Layout+Motion pair per beat (keyed by beatId). This is the object a
|
|
253
|
-
* checkpoint returns and the compiler consumes. */
|
|
254
|
-
export const GraphicsPlan = z
|
|
255
|
-
.object({
|
|
256
|
-
version: Version,
|
|
257
|
-
content: ContentPlan,
|
|
258
|
-
layouts: z.array(LayoutPlan),
|
|
259
|
-
motions: z.array(MotionPlan),
|
|
260
|
-
})
|
|
261
|
-
.strict();
|