@nodaro/shared 1.4.2
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 +202 -0
- package/README.md +32 -0
- package/dist/index.cjs +12506 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12269 -0
- package/dist/index.d.ts +12269 -0
- package/dist/index.js +11831 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/src/__tests__/__snapshots__/prompt-builder-segments.test.ts.snap +65 -0
- package/src/__tests__/ai-avatar-pricing.test.ts +321 -0
- package/src/__tests__/apply-video-negative-prompt.test.ts +76 -0
- package/src/__tests__/attach-columns.test.ts +28 -0
- package/src/__tests__/audio-crossfade-curves.test.ts +33 -0
- package/src/__tests__/build-model-menu.test.ts +58 -0
- package/src/__tests__/character-aspect-defaults.test.ts +142 -0
- package/src/__tests__/character-mention-slug.test.ts +199 -0
- package/src/__tests__/character-reference-photo-kinds.test.ts +32 -0
- package/src/__tests__/character-usage-mode.test.ts +147 -0
- package/src/__tests__/character-variant-assets.test.ts +142 -0
- package/src/__tests__/character-voice.test.ts +130 -0
- package/src/__tests__/cinematic-avatar-pricing.test.ts +119 -0
- package/src/__tests__/combine-transitions.test.ts +137 -0
- package/src/__tests__/component-types.test.ts +87 -0
- package/src/__tests__/condition-variables.test.ts +98 -0
- package/src/__tests__/credit-identifiers.test.ts +811 -0
- package/src/__tests__/default-video-provider.test.ts +63 -0
- package/src/__tests__/entity-image-handle.test.ts +43 -0
- package/src/__tests__/entity-variants.test.ts +33 -0
- package/src/__tests__/fal-lip-sync-sets.test.ts +31 -0
- package/src/__tests__/featured-entities.test.ts +33 -0
- package/src/__tests__/film-pricing.test.ts +64 -0
- package/src/__tests__/filter-condition.test.ts +285 -0
- package/src/__tests__/freecut-protocol.test.ts +35 -0
- package/src/__tests__/generate-text-items.test.ts +19 -0
- package/src/__tests__/generated-results.test.ts +50 -0
- package/src/__tests__/graph-utils.test.ts +618 -0
- package/src/__tests__/group-aggregation.test.ts +83 -0
- package/src/__tests__/i18n-locale-completeness.test.ts +156 -0
- package/src/__tests__/image-critic-node.test.ts +86 -0
- package/src/__tests__/image-mask-mode.test.ts +16 -0
- package/src/__tests__/image-ref-limit.test.ts +78 -0
- package/src/__tests__/inpaint-region.test.ts +24 -0
- package/src/__tests__/input-field-map-parity.test.ts +30 -0
- package/src/__tests__/json-evaluator.test.ts +289 -0
- package/src/__tests__/json-path.test.ts +64 -0
- package/src/__tests__/lip-sync-pricing.test.ts +142 -0
- package/src/__tests__/list-merge.test.ts +96 -0
- package/src/__tests__/list-sort.test.ts +134 -0
- package/src/__tests__/llm-models.test.ts +445 -0
- package/src/__tests__/location-mention-slug.test.ts +246 -0
- package/src/__tests__/locations-coverage-critic-schema.test.ts +92 -0
- package/src/__tests__/loop-delimiter.test.ts +39 -0
- package/src/__tests__/lottie-slots.test.ts +245 -0
- package/src/__tests__/model-catalog-flux2-resolutions.test.ts +21 -0
- package/src/__tests__/model-constants-location.test.ts +20 -0
- package/src/__tests__/model-constants.test.ts +122 -0
- package/src/__tests__/model-tree.test.ts +86 -0
- package/src/__tests__/no-internal-imports.test.ts +25 -0
- package/src/__tests__/node-default-mappings.test.ts +175 -0
- package/src/__tests__/node-mappable-fields.test.ts +7 -0
- package/src/__tests__/node-preset-export.test.ts +46 -0
- package/src/__tests__/node-preset-extract.test.ts +166 -0
- package/src/__tests__/node-refs-canonical.test.ts +54 -0
- package/src/__tests__/node-refs-combine.test.ts +54 -0
- package/src/__tests__/node-refs.test.ts +187 -0
- package/src/__tests__/node-runtime-keys.test.ts +99 -0
- package/src/__tests__/object-aspect-defaults.test.ts +59 -0
- package/src/__tests__/parameter-node-value.test.ts +137 -0
- package/src/__tests__/pinterest-url.test.ts +35 -0
- package/src/__tests__/pipeline-chat.test.ts +110 -0
- package/src/__tests__/pipeline-validation.test.ts +98 -0
- package/src/__tests__/presentation-utils.test.ts +1137 -0
- package/src/__tests__/producer-types.test.ts +47 -0
- package/src/__tests__/prompt-length-limits.test.ts +156 -0
- package/src/__tests__/provider-directive-defaults.test.ts +46 -0
- package/src/__tests__/reduce-strategy-registry.test.ts +43 -0
- package/src/__tests__/reference-board-templates.test.ts +33 -0
- package/src/__tests__/reference-roles.test.ts +36 -0
- package/src/__tests__/resolve-field-mappings.test.ts +110 -0
- package/src/__tests__/resolve-pipeline-model.test.ts +76 -0
- package/src/__tests__/scene-node-types.test.ts +80 -0
- package/src/__tests__/scraper-actors.test.ts +67 -0
- package/src/__tests__/seedance-2-catalog.test.ts +18 -0
- package/src/__tests__/seedance-2-credit-id.test.ts +23 -0
- package/src/__tests__/seedance-aspects.test.ts +52 -0
- package/src/__tests__/selector.test.ts +1151 -0
- package/src/__tests__/settled-with-limit.test.ts +214 -0
- package/src/__tests__/social-media-specs.test.ts +129 -0
- package/src/__tests__/sub-workflow-validation.test.ts +88 -0
- package/src/__tests__/switchx-pricing.test.ts +28 -0
- package/src/__tests__/to-connected-references.test.ts +153 -0
- package/src/__tests__/utilities.test.ts +541 -0
- package/src/__tests__/video-analysis-pricing.test.ts +58 -0
- package/src/__tests__/video-analysis.test.ts +63 -0
- package/src/__tests__/video-audio-capability.test.ts +204 -0
- package/src/__tests__/video-gen-providers.test.ts +28 -0
- package/src/__tests__/video-mode-aliases.test.ts +150 -0
- package/src/__tests__/video-ref-limits.test.ts +72 -0
- package/src/__tests__/video-utils.test.ts +303 -0
- package/src/__tests__/voice-changer-models.test.ts +33 -0
- package/src/__tests__/workflow-export-strip.test.ts +46 -0
- package/src/ai-avatar-pricing.ts +213 -0
- package/src/ancestor-refs.ts +48 -0
- package/src/animals.ts +231 -0
- package/src/audio-crossfade-curves.ts +73 -0
- package/src/audio-fx-presets.ts +64 -0
- package/src/avatar-payload-validators.ts +214 -0
- package/src/caption-styles.ts +31 -0
- package/src/character-aspect-defaults.ts +102 -0
- package/src/character-facets.ts +37 -0
- package/src/character-mention-slug.ts +166 -0
- package/src/character-usage-mode.ts +109 -0
- package/src/character-variant-assets.ts +121 -0
- package/src/character-voice.ts +151 -0
- package/src/cinematic-avatar-pricing.ts +111 -0
- package/src/clone-utils.ts +46 -0
- package/src/combine-transitions.ts +482 -0
- package/src/community.ts +112 -0
- package/src/component-types.ts +127 -0
- package/src/condition-variables.ts +65 -0
- package/src/credit-estimators/index.ts +15 -0
- package/src/credit-estimators/video-utils.ts +165 -0
- package/src/credit-identifiers.ts +304 -0
- package/src/entity-approval-types.ts +31 -0
- package/src/entity-aspect-defaults.ts +82 -0
- package/src/entity-asset-types.ts +296 -0
- package/src/entity-image-handle.ts +36 -0
- package/src/extra-refs.ts +139 -0
- package/src/featured-entities.ts +136 -0
- package/src/film-pricing.ts +117 -0
- package/src/filter-condition.ts +250 -0
- package/src/flux2-pricing.ts +18 -0
- package/src/freecut-protocol.ts +52 -0
- package/src/furniture.ts +167 -0
- package/src/generate-text-items.ts +15 -0
- package/src/generated-results.ts +63 -0
- package/src/group-aggregation.ts +75 -0
- package/src/hint-graph-types.ts +18 -0
- package/src/i18n/action-fx.ar.ts +89 -0
- package/src/i18n/action-fx.de.ts +89 -0
- package/src/i18n/action-fx.es.ts +89 -0
- package/src/i18n/action-fx.fr.ts +89 -0
- package/src/i18n/action-fx.he.ts +89 -0
- package/src/i18n/action-fx.hi.ts +89 -0
- package/src/i18n/action-fx.ja.ts +89 -0
- package/src/i18n/action-fx.ko.ts +89 -0
- package/src/i18n/action-fx.pt-BR.ts +89 -0
- package/src/i18n/action-fx.ru.ts +89 -0
- package/src/i18n/action-fx.zh-CN.ts +89 -0
- package/src/i18n/aesthetic.ar.ts +170 -0
- package/src/i18n/aesthetic.de.ts +48 -0
- package/src/i18n/aesthetic.es.ts +169 -0
- package/src/i18n/aesthetic.fr.ts +52 -0
- package/src/i18n/aesthetic.he.ts +169 -0
- package/src/i18n/aesthetic.hi.ts +55 -0
- package/src/i18n/aesthetic.ja.ts +165 -0
- package/src/i18n/aesthetic.ko.ts +169 -0
- package/src/i18n/aesthetic.pt-BR.ts +148 -0
- package/src/i18n/aesthetic.ru.ts +155 -0
- package/src/i18n/aesthetic.zh-CN.ts +172 -0
- package/src/i18n/animals.ar.ts +145 -0
- package/src/i18n/animals.de.ts +144 -0
- package/src/i18n/animals.es.ts +158 -0
- package/src/i18n/animals.fr.ts +144 -0
- package/src/i18n/animals.he.ts +155 -0
- package/src/i18n/animals.hi.ts +157 -0
- package/src/i18n/animals.ja.ts +159 -0
- package/src/i18n/animals.ko.ts +159 -0
- package/src/i18n/animals.pt-BR.ts +157 -0
- package/src/i18n/animals.ru.ts +155 -0
- package/src/i18n/animals.zh-CN.ts +155 -0
- package/src/i18n/atmosphere.ar.ts +49 -0
- package/src/i18n/atmosphere.de.ts +47 -0
- package/src/i18n/atmosphere.es.ts +50 -0
- package/src/i18n/atmosphere.fr.ts +47 -0
- package/src/i18n/atmosphere.he.ts +47 -0
- package/src/i18n/atmosphere.hi.ts +49 -0
- package/src/i18n/atmosphere.ja.ts +49 -0
- package/src/i18n/atmosphere.ko.ts +49 -0
- package/src/i18n/atmosphere.pt-BR.ts +49 -0
- package/src/i18n/atmosphere.ru.ts +47 -0
- package/src/i18n/atmosphere.zh-CN.ts +152 -0
- package/src/i18n/backdrop.ar.ts +53 -0
- package/src/i18n/backdrop.de.ts +46 -0
- package/src/i18n/backdrop.es.ts +54 -0
- package/src/i18n/backdrop.fr.ts +52 -0
- package/src/i18n/backdrop.he.ts +51 -0
- package/src/i18n/backdrop.hi.ts +59 -0
- package/src/i18n/backdrop.ja.ts +61 -0
- package/src/i18n/backdrop.ko.ts +59 -0
- package/src/i18n/backdrop.pt-BR.ts +59 -0
- package/src/i18n/backdrop.ru.ts +57 -0
- package/src/i18n/backdrop.zh-CN.ts +57 -0
- package/src/i18n/camera-format.ar.ts +40 -0
- package/src/i18n/camera-format.de.ts +37 -0
- package/src/i18n/camera-format.es.ts +41 -0
- package/src/i18n/camera-format.fr.ts +37 -0
- package/src/i18n/camera-format.he.ts +37 -0
- package/src/i18n/camera-format.hi.ts +42 -0
- package/src/i18n/camera-format.ja.ts +37 -0
- package/src/i18n/camera-format.ko.ts +37 -0
- package/src/i18n/camera-format.pt-BR.ts +46 -0
- package/src/i18n/camera-format.ru.ts +42 -0
- package/src/i18n/camera-format.zh-CN.ts +42 -0
- package/src/i18n/camera-motions.ar.ts +86 -0
- package/src/i18n/camera-motions.de.ts +74 -0
- package/src/i18n/camera-motions.es.ts +86 -0
- package/src/i18n/camera-motions.fr.ts +86 -0
- package/src/i18n/camera-motions.he.ts +86 -0
- package/src/i18n/camera-motions.hi.ts +99 -0
- package/src/i18n/camera-motions.ja.ts +99 -0
- package/src/i18n/camera-motions.ko.ts +99 -0
- package/src/i18n/camera-motions.pt-BR.ts +99 -0
- package/src/i18n/camera-motions.ru.ts +99 -0
- package/src/i18n/camera-motions.zh-CN.ts +99 -0
- package/src/i18n/character-fx.ar.ts +72 -0
- package/src/i18n/character-fx.de.ts +72 -0
- package/src/i18n/character-fx.es.ts +72 -0
- package/src/i18n/character-fx.fr.ts +72 -0
- package/src/i18n/character-fx.he.ts +72 -0
- package/src/i18n/character-fx.hi.ts +72 -0
- package/src/i18n/character-fx.ja.ts +72 -0
- package/src/i18n/character-fx.ko.ts +72 -0
- package/src/i18n/character-fx.pt-BR.ts +72 -0
- package/src/i18n/character-fx.ru.ts +72 -0
- package/src/i18n/character-fx.zh-CN.ts +72 -0
- package/src/i18n/color-look.ar.ts +50 -0
- package/src/i18n/color-look.de.ts +47 -0
- package/src/i18n/color-look.es.ts +50 -0
- package/src/i18n/color-look.fr.ts +50 -0
- package/src/i18n/color-look.he.ts +49 -0
- package/src/i18n/color-look.hi.ts +52 -0
- package/src/i18n/color-look.ja.ts +54 -0
- package/src/i18n/color-look.ko.ts +54 -0
- package/src/i18n/color-look.pt-BR.ts +54 -0
- package/src/i18n/color-look.ru.ts +52 -0
- package/src/i18n/color-look.zh-CN.ts +53 -0
- package/src/i18n/composition-effects.ar.ts +26 -0
- package/src/i18n/composition-effects.de.ts +25 -0
- package/src/i18n/composition-effects.es.ts +27 -0
- package/src/i18n/composition-effects.fr.ts +25 -0
- package/src/i18n/composition-effects.he.ts +25 -0
- package/src/i18n/composition-effects.hi.ts +27 -0
- package/src/i18n/composition-effects.ja.ts +27 -0
- package/src/i18n/composition-effects.ko.ts +27 -0
- package/src/i18n/composition-effects.pt-BR.ts +25 -0
- package/src/i18n/composition-effects.ru.ts +82 -0
- package/src/i18n/composition-effects.zh-CN.ts +25 -0
- package/src/i18n/era.ar.ts +37 -0
- package/src/i18n/era.de.ts +34 -0
- package/src/i18n/era.es.ts +37 -0
- package/src/i18n/era.fr.ts +37 -0
- package/src/i18n/era.he.ts +36 -0
- package/src/i18n/era.hi.ts +39 -0
- package/src/i18n/era.ja.ts +41 -0
- package/src/i18n/era.ko.ts +39 -0
- package/src/i18n/era.pt-BR.ts +41 -0
- package/src/i18n/era.ru.ts +121 -0
- package/src/i18n/era.zh-CN.ts +39 -0
- package/src/i18n/exposure-settings.ar.ts +28 -0
- package/src/i18n/exposure-settings.de.ts +26 -0
- package/src/i18n/exposure-settings.es.ts +28 -0
- package/src/i18n/exposure-settings.fr.ts +29 -0
- package/src/i18n/exposure-settings.he.ts +28 -0
- package/src/i18n/exposure-settings.hi.ts +31 -0
- package/src/i18n/exposure-settings.ja.ts +31 -0
- package/src/i18n/exposure-settings.ko.ts +31 -0
- package/src/i18n/exposure-settings.pt-BR.ts +31 -0
- package/src/i18n/exposure-settings.ru.ts +31 -0
- package/src/i18n/exposure-settings.zh-CN.ts +31 -0
- package/src/i18n/framing.ar.ts +78 -0
- package/src/i18n/framing.de.ts +73 -0
- package/src/i18n/framing.es.ts +78 -0
- package/src/i18n/framing.fr.ts +78 -0
- package/src/i18n/framing.he.ts +77 -0
- package/src/i18n/framing.hi.ts +82 -0
- package/src/i18n/framing.ja.ts +84 -0
- package/src/i18n/framing.ko.ts +82 -0
- package/src/i18n/framing.pt-BR.ts +84 -0
- package/src/i18n/framing.ru.ts +82 -0
- package/src/i18n/framing.zh-CN.ts +82 -0
- package/src/i18n/furniture.ar.ts +92 -0
- package/src/i18n/furniture.de.ts +93 -0
- package/src/i18n/furniture.es.ts +101 -0
- package/src/i18n/furniture.fr.ts +93 -0
- package/src/i18n/furniture.he.ts +101 -0
- package/src/i18n/furniture.hi.ts +101 -0
- package/src/i18n/furniture.ja.ts +101 -0
- package/src/i18n/furniture.ko.ts +101 -0
- package/src/i18n/furniture.pt-BR.ts +101 -0
- package/src/i18n/furniture.ru.ts +101 -0
- package/src/i18n/furniture.zh-CN.ts +101 -0
- package/src/i18n/held-prop.ar.ts +74 -0
- package/src/i18n/held-prop.de.ts +65 -0
- package/src/i18n/held-prop.es.ts +83 -0
- package/src/i18n/held-prop.fr.ts +74 -0
- package/src/i18n/held-prop.he.ts +73 -0
- package/src/i18n/held-prop.hi.ts +82 -0
- package/src/i18n/held-prop.ja.ts +84 -0
- package/src/i18n/held-prop.ko.ts +84 -0
- package/src/i18n/held-prop.pt-BR.ts +84 -0
- package/src/i18n/held-prop.ru.ts +82 -0
- package/src/i18n/held-prop.zh-CN.ts +82 -0
- package/src/i18n/index.ts +208 -0
- package/src/i18n/instrumentation.ar.ts +195 -0
- package/src/i18n/instrumentation.de.ts +195 -0
- package/src/i18n/instrumentation.es.ts +195 -0
- package/src/i18n/instrumentation.fr.ts +195 -0
- package/src/i18n/instrumentation.he.ts +195 -0
- package/src/i18n/instrumentation.hi.ts +195 -0
- package/src/i18n/instrumentation.ja.ts +195 -0
- package/src/i18n/instrumentation.ko.ts +195 -0
- package/src/i18n/instrumentation.pt-BR.ts +195 -0
- package/src/i18n/instrumentation.ru.ts +195 -0
- package/src/i18n/instrumentation.zh-CN.ts +195 -0
- package/src/i18n/lens.ar.ts +24 -0
- package/src/i18n/lens.de.ts +24 -0
- package/src/i18n/lens.es.ts +24 -0
- package/src/i18n/lens.fr.ts +24 -0
- package/src/i18n/lens.he.ts +24 -0
- package/src/i18n/lens.hi.ts +24 -0
- package/src/i18n/lens.ja.ts +24 -0
- package/src/i18n/lens.ko.ts +24 -0
- package/src/i18n/lens.pt-BR.ts +24 -0
- package/src/i18n/lens.ru.ts +25 -0
- package/src/i18n/lens.zh-CN.ts +24 -0
- package/src/i18n/lighting.ar.ts +86 -0
- package/src/i18n/lighting.de.ts +84 -0
- package/src/i18n/lighting.es.ts +89 -0
- package/src/i18n/lighting.fr.ts +84 -0
- package/src/i18n/lighting.he.ts +88 -0
- package/src/i18n/lighting.hi.ts +90 -0
- package/src/i18n/lighting.ja.ts +90 -0
- package/src/i18n/lighting.ko.ts +90 -0
- package/src/i18n/lighting.pt-BR.ts +90 -0
- package/src/i18n/lighting.ru.ts +88 -0
- package/src/i18n/lighting.zh-CN.ts +92 -0
- package/src/i18n/loop-subject.ar.ts +39 -0
- package/src/i18n/loop-subject.de.ts +39 -0
- package/src/i18n/loop-subject.es.ts +39 -0
- package/src/i18n/loop-subject.fr.ts +39 -0
- package/src/i18n/loop-subject.he.ts +39 -0
- package/src/i18n/loop-subject.hi.ts +39 -0
- package/src/i18n/loop-subject.ja.ts +39 -0
- package/src/i18n/loop-subject.ko.ts +39 -0
- package/src/i18n/loop-subject.pt-BR.ts +39 -0
- package/src/i18n/loop-subject.ru.ts +39 -0
- package/src/i18n/loop-subject.zh-CN.ts +39 -0
- package/src/i18n/materials.ar.ts +80 -0
- package/src/i18n/materials.de.ts +80 -0
- package/src/i18n/materials.es.ts +87 -0
- package/src/i18n/materials.fr.ts +80 -0
- package/src/i18n/materials.he.ts +86 -0
- package/src/i18n/materials.hi.ts +86 -0
- package/src/i18n/materials.ja.ts +88 -0
- package/src/i18n/materials.ko.ts +88 -0
- package/src/i18n/materials.pt-BR.ts +88 -0
- package/src/i18n/materials.ru.ts +86 -0
- package/src/i18n/materials.zh-CN.ts +86 -0
- package/src/i18n/mood.ar.ts +61 -0
- package/src/i18n/mood.de.ts +61 -0
- package/src/i18n/mood.es.ts +66 -0
- package/src/i18n/mood.fr.ts +61 -0
- package/src/i18n/mood.he.ts +64 -0
- package/src/i18n/mood.hi.ts +66 -0
- package/src/i18n/mood.ja.ts +66 -0
- package/src/i18n/mood.ko.ts +66 -0
- package/src/i18n/mood.pt-BR.ts +64 -0
- package/src/i18n/mood.ru.ts +64 -0
- package/src/i18n/mood.zh-CN.ts +64 -0
- package/src/i18n/music-genre.ar.ts +302 -0
- package/src/i18n/music-genre.de.ts +302 -0
- package/src/i18n/music-genre.es.ts +302 -0
- package/src/i18n/music-genre.fr.ts +302 -0
- package/src/i18n/music-genre.he.ts +302 -0
- package/src/i18n/music-genre.hi.ts +302 -0
- package/src/i18n/music-genre.ja.ts +302 -0
- package/src/i18n/music-genre.ko.ts +302 -0
- package/src/i18n/music-genre.pt-BR.ts +302 -0
- package/src/i18n/music-genre.ru.ts +302 -0
- package/src/i18n/music-genre.zh-CN.ts +302 -0
- package/src/i18n/music-mood.ar.ts +75 -0
- package/src/i18n/music-mood.de.ts +75 -0
- package/src/i18n/music-mood.es.ts +75 -0
- package/src/i18n/music-mood.fr.ts +75 -0
- package/src/i18n/music-mood.he.ts +75 -0
- package/src/i18n/music-mood.hi.ts +75 -0
- package/src/i18n/music-mood.ja.ts +75 -0
- package/src/i18n/music-mood.ko.ts +75 -0
- package/src/i18n/music-mood.pt-BR.ts +75 -0
- package/src/i18n/music-mood.ru.ts +75 -0
- package/src/i18n/music-mood.zh-CN.ts +75 -0
- package/src/i18n/person.ar.ts +638 -0
- package/src/i18n/person.de.ts +629 -0
- package/src/i18n/person.es.ts +645 -0
- package/src/i18n/person.fr.ts +621 -0
- package/src/i18n/person.he.ts +656 -0
- package/src/i18n/person.hi.ts +654 -0
- package/src/i18n/person.ja.ts +662 -0
- package/src/i18n/person.ko.ts +662 -0
- package/src/i18n/person.pt-BR.ts +656 -0
- package/src/i18n/person.ru.ts +656 -0
- package/src/i18n/person.zh-CN.ts +656 -0
- package/src/i18n/photo-genre.ar.ts +58 -0
- package/src/i18n/photo-genre.de.ts +52 -0
- package/src/i18n/photo-genre.es.ts +64 -0
- package/src/i18n/photo-genre.fr.ts +58 -0
- package/src/i18n/photo-genre.he.ts +62 -0
- package/src/i18n/photo-genre.hi.ts +63 -0
- package/src/i18n/photo-genre.ja.ts +65 -0
- package/src/i18n/photo-genre.ko.ts +63 -0
- package/src/i18n/photo-genre.pt-BR.ts +65 -0
- package/src/i18n/photo-genre.ru.ts +63 -0
- package/src/i18n/photo-genre.zh-CN.ts +63 -0
- package/src/i18n/photographer.ar.ts +75 -0
- package/src/i18n/photographer.de.ts +74 -0
- package/src/i18n/photographer.es.ts +77 -0
- package/src/i18n/photographer.fr.ts +77 -0
- package/src/i18n/photographer.he.ts +78 -0
- package/src/i18n/photographer.hi.ts +80 -0
- package/src/i18n/photographer.ja.ts +84 -0
- package/src/i18n/photographer.ko.ts +84 -0
- package/src/i18n/photographer.pt-BR.ts +83 -0
- package/src/i18n/photographer.ru.ts +78 -0
- package/src/i18n/photographer.zh-CN.ts +78 -0
- package/src/i18n/pose.ar.ts +95 -0
- package/src/i18n/pose.de.ts +96 -0
- package/src/i18n/pose.es.ts +104 -0
- package/src/i18n/pose.fr.ts +96 -0
- package/src/i18n/pose.he.ts +104 -0
- package/src/i18n/pose.hi.ts +104 -0
- package/src/i18n/pose.ja.ts +104 -0
- package/src/i18n/pose.ko.ts +104 -0
- package/src/i18n/pose.pt-BR.ts +104 -0
- package/src/i18n/pose.ru.ts +104 -0
- package/src/i18n/pose.zh-CN.ts +104 -0
- package/src/i18n/post-process-effects.ar.ts +25 -0
- package/src/i18n/post-process-effects.de.ts +24 -0
- package/src/i18n/post-process-effects.es.ts +26 -0
- package/src/i18n/post-process-effects.fr.ts +24 -0
- package/src/i18n/post-process-effects.he.ts +24 -0
- package/src/i18n/post-process-effects.hi.ts +26 -0
- package/src/i18n/post-process-effects.ja.ts +26 -0
- package/src/i18n/post-process-effects.ko.ts +26 -0
- package/src/i18n/post-process-effects.pt-BR.ts +24 -0
- package/src/i18n/post-process-effects.ru.ts +24 -0
- package/src/i18n/post-process-effects.zh-CN.ts +24 -0
- package/src/i18n/render-quality.ar.ts +34 -0
- package/src/i18n/render-quality.de.ts +30 -0
- package/src/i18n/render-quality.es.ts +35 -0
- package/src/i18n/render-quality.fr.ts +34 -0
- package/src/i18n/render-quality.he.ts +33 -0
- package/src/i18n/render-quality.hi.ts +39 -0
- package/src/i18n/render-quality.ja.ts +39 -0
- package/src/i18n/render-quality.ko.ts +39 -0
- package/src/i18n/render-quality.pt-BR.ts +37 -0
- package/src/i18n/render-quality.ru.ts +37 -0
- package/src/i18n/render-quality.zh-CN.ts +37 -0
- package/src/i18n/seasons.ar.ts +10 -0
- package/src/i18n/seasons.de.ts +10 -0
- package/src/i18n/seasons.es.ts +10 -0
- package/src/i18n/seasons.fr.ts +10 -0
- package/src/i18n/seasons.he.ts +10 -0
- package/src/i18n/seasons.hi.ts +10 -0
- package/src/i18n/seasons.ja.ts +10 -0
- package/src/i18n/seasons.ko.ts +10 -0
- package/src/i18n/seasons.pt-BR.ts +10 -0
- package/src/i18n/seasons.ru.ts +10 -0
- package/src/i18n/seasons.zh-CN.ts +10 -0
- package/src/i18n/setting.ar.ts +73 -0
- package/src/i18n/setting.de.ts +73 -0
- package/src/i18n/setting.es.ts +77 -0
- package/src/i18n/setting.fr.ts +73 -0
- package/src/i18n/setting.he.ts +76 -0
- package/src/i18n/setting.hi.ts +76 -0
- package/src/i18n/setting.ja.ts +78 -0
- package/src/i18n/setting.ko.ts +76 -0
- package/src/i18n/setting.pt-BR.ts +78 -0
- package/src/i18n/setting.ru.ts +76 -0
- package/src/i18n/setting.zh-CN.ts +76 -0
- package/src/i18n/style.ar.ts +55 -0
- package/src/i18n/style.de.ts +54 -0
- package/src/i18n/style.es.ts +54 -0
- package/src/i18n/style.fr.ts +54 -0
- package/src/i18n/style.he.ts +54 -0
- package/src/i18n/style.hi.ts +54 -0
- package/src/i18n/style.ja.ts +54 -0
- package/src/i18n/style.ko.ts +54 -0
- package/src/i18n/style.pt-BR.ts +56 -0
- package/src/i18n/style.ru.ts +54 -0
- package/src/i18n/style.zh-CN.ts +54 -0
- package/src/i18n/styling.ar.ts +295 -0
- package/src/i18n/styling.de.ts +285 -0
- package/src/i18n/styling.es.ts +317 -0
- package/src/i18n/styling.fr.ts +285 -0
- package/src/i18n/styling.he.ts +307 -0
- package/src/i18n/styling.hi.ts +323 -0
- package/src/i18n/styling.ja.ts +317 -0
- package/src/i18n/styling.ko.ts +311 -0
- package/src/i18n/styling.pt-BR.ts +301 -0
- package/src/i18n/styling.ru.ts +301 -0
- package/src/i18n/styling.zh-CN.ts +307 -0
- package/src/i18n/temporal.ar.ts +31 -0
- package/src/i18n/temporal.de.ts +24 -0
- package/src/i18n/temporal.es.ts +27 -0
- package/src/i18n/temporal.fr.ts +28 -0
- package/src/i18n/temporal.he.ts +31 -0
- package/src/i18n/temporal.hi.ts +31 -0
- package/src/i18n/temporal.ja.ts +31 -0
- package/src/i18n/temporal.ko.ts +31 -0
- package/src/i18n/temporal.pt-BR.ts +31 -0
- package/src/i18n/temporal.ru.ts +31 -0
- package/src/i18n/temporal.zh-CN.ts +31 -0
- package/src/i18n/transitions.ar.ts +97 -0
- package/src/i18n/transitions.de.ts +97 -0
- package/src/i18n/transitions.es.ts +97 -0
- package/src/i18n/transitions.fr.ts +97 -0
- package/src/i18n/transitions.he.ts +97 -0
- package/src/i18n/transitions.hi.ts +97 -0
- package/src/i18n/transitions.ja.ts +97 -0
- package/src/i18n/transitions.ko.ts +97 -0
- package/src/i18n/transitions.pt-BR.ts +97 -0
- package/src/i18n/transitions.ru.ts +97 -0
- package/src/i18n/transitions.zh-CN.ts +97 -0
- package/src/i18n/types.ts +158 -0
- package/src/i18n/vehicles.ar.ts +137 -0
- package/src/i18n/vehicles.de.ts +125 -0
- package/src/i18n/vehicles.es.ts +138 -0
- package/src/i18n/vehicles.fr.ts +125 -0
- package/src/i18n/vehicles.he.ts +136 -0
- package/src/i18n/vehicles.hi.ts +138 -0
- package/src/i18n/vehicles.ja.ts +138 -0
- package/src/i18n/vehicles.ko.ts +138 -0
- package/src/i18n/vehicles.pt-BR.ts +136 -0
- package/src/i18n/vehicles.ru.ts +136 -0
- package/src/i18n/vehicles.zh-CN.ts +136 -0
- package/src/i18n/voice-character.ar.ts +126 -0
- package/src/i18n/voice-character.de.ts +126 -0
- package/src/i18n/voice-character.es.ts +126 -0
- package/src/i18n/voice-character.fr.ts +126 -0
- package/src/i18n/voice-character.he.ts +126 -0
- package/src/i18n/voice-character.hi.ts +126 -0
- package/src/i18n/voice-character.ja.ts +126 -0
- package/src/i18n/voice-character.ko.ts +126 -0
- package/src/i18n/voice-character.pt-BR.ts +126 -0
- package/src/i18n/voice-character.ru.ts +126 -0
- package/src/i18n/voice-character.zh-CN.ts +126 -0
- package/src/i18n/voice-delivery.ar.ts +77 -0
- package/src/i18n/voice-delivery.de.ts +77 -0
- package/src/i18n/voice-delivery.es.ts +77 -0
- package/src/i18n/voice-delivery.fr.ts +77 -0
- package/src/i18n/voice-delivery.he.ts +77 -0
- package/src/i18n/voice-delivery.hi.ts +77 -0
- package/src/i18n/voice-delivery.ja.ts +77 -0
- package/src/i18n/voice-delivery.ko.ts +77 -0
- package/src/i18n/voice-delivery.pt-BR.ts +77 -0
- package/src/i18n/voice-delivery.ru.ts +77 -0
- package/src/i18n/voice-delivery.zh-CN.ts +77 -0
- package/src/i18n/weapons.ar.ts +115 -0
- package/src/i18n/weapons.de.ts +114 -0
- package/src/i18n/weapons.es.ts +116 -0
- package/src/i18n/weapons.fr.ts +103 -0
- package/src/i18n/weapons.he.ts +114 -0
- package/src/i18n/weapons.hi.ts +116 -0
- package/src/i18n/weapons.ja.ts +116 -0
- package/src/i18n/weapons.ko.ts +116 -0
- package/src/i18n/weapons.pt-BR.ts +114 -0
- package/src/i18n/weapons.ru.ts +114 -0
- package/src/i18n/weapons.zh-CN.ts +114 -0
- package/src/image-critic-node.ts +61 -0
- package/src/image-critic-types.ts +40 -0
- package/src/index.ts +866 -0
- package/src/inject-upstream.ts +15 -0
- package/src/inpaint-region.ts +51 -0
- package/src/json-evaluator.ts +732 -0
- package/src/json-path.ts +52 -0
- package/src/lip-sync-pricing.ts +90 -0
- package/src/list-merge.ts +40 -0
- package/src/list-sort.ts +122 -0
- package/src/list-source-resolver.ts +50 -0
- package/src/llm-models.ts +245 -0
- package/src/location-mention-slug.ts +291 -0
- package/src/location-preset-catalog-map.ts +128 -0
- package/src/loop-delimiter.ts +73 -0
- package/src/lora-routing.ts +95 -0
- package/src/lottie-overlay-catalog.ts +175 -0
- package/src/lottie-slots.ts +309 -0
- package/src/model-catalog.ts +2386 -0
- package/src/model-constants.ts +1910 -0
- package/src/model-tree.ts +120 -0
- package/src/monetization.ts +15 -0
- package/src/multi-pick.ts +42 -0
- package/src/node-default-mappings.ts +262 -0
- package/src/node-mappable-fields.ts +70 -0
- package/src/node-preset-export.ts +56 -0
- package/src/node-preset-extract.ts +147 -0
- package/src/node-refs.ts +253 -0
- package/src/node-runtime-keys.ts +117 -0
- package/src/object-aspect-defaults.ts +72 -0
- package/src/object-picker-types.ts +22 -0
- package/src/parameter-node-value.ts +284 -0
- package/src/pinterest-url.ts +28 -0
- package/src/pipeline-chat.ts +72 -0
- package/src/pipeline-defaults.ts +318 -0
- package/src/pipeline-events.ts +162 -0
- package/src/pipeline-state-types.ts +196 -0
- package/src/pipeline-types.ts +647 -0
- package/src/pipeline-validation.ts +87 -0
- package/src/presentation-types.ts +23 -0
- package/src/presentation-utils.ts +397 -0
- package/src/producer-types.ts +188 -0
- package/src/progress-curve.ts +99 -0
- package/src/provider-directive-defaults.ts +69 -0
- package/src/reduce-strategy-registry.ts +121 -0
- package/src/reference-board-templates.ts +81 -0
- package/src/reference-roles.ts +153 -0
- package/src/reference-sheet/__tests__/board-to-column.test.ts +20 -0
- package/src/reference-sheet/__tests__/catalog.test.ts +50 -0
- package/src/reference-sheet/__tests__/cost.test.ts +38 -0
- package/src/reference-sheet/__tests__/no-drift.test.ts +28 -0
- package/src/reference-sheet/__tests__/panel-plan.test.ts +61 -0
- package/src/reference-sheet/__tests__/panel-prompts.test.ts +17 -0
- package/src/reference-sheet/__tests__/plan-generation.test.ts +72 -0
- package/src/reference-sheet/__tests__/presets.test.ts +54 -0
- package/src/reference-sheet/__tests__/types.test.ts +24 -0
- package/src/reference-sheet/catalog.ts +144 -0
- package/src/reference-sheet/cost.ts +37 -0
- package/src/reference-sheet/credit-id.ts +7 -0
- package/src/reference-sheet/index.ts +8 -0
- package/src/reference-sheet/panel-plan.ts +65 -0
- package/src/reference-sheet/panel-prompts.ts +14 -0
- package/src/reference-sheet/plan-generation.ts +79 -0
- package/src/reference-sheet/presets.ts +77 -0
- package/src/reference-sheet/types.ts +92 -0
- package/src/repeat-types.ts +97 -0
- package/src/resolve-field-mappings.ts +119 -0
- package/src/route-filter.ts +80 -0
- package/src/scene-helper-types.ts +249 -0
- package/src/scene-node-types.ts +281 -0
- package/src/scraper-actors.ts +57 -0
- package/src/scraper-output-schemas.ts +25 -0
- package/src/seasons.ts +45 -0
- package/src/selector.ts +864 -0
- package/src/settled-with-limit.ts +42 -0
- package/src/shot-element.ts +43 -0
- package/src/social-media-specs.ts +121 -0
- package/src/social-post.ts +24 -0
- package/src/sub-workflow-validation.ts +78 -0
- package/src/supported-fonts.ts +40 -0
- package/src/surround.ts +113 -0
- package/src/switchx-pricing.ts +33 -0
- package/src/text-separators.ts +59 -0
- package/src/to-connected-references.ts +77 -0
- package/src/types.ts +272 -0
- package/src/variant-job-id.ts +17 -0
- package/src/vehicles.ts +213 -0
- package/src/video-analysis-pricing.ts +74 -0
- package/src/video-analysis.ts +127 -0
- package/src/video-duration.ts +25 -0
- package/src/voice-changer-models.ts +36 -0
- package/src/voice-matcher-types.ts +37 -0
- package/src/voices.ts +92 -0
- package/src/weapons.ts +190 -0
- package/src/workflow-export.ts +153 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { EXECUTION_DATA_KEYS } from "./node-runtime-keys.js"
|
|
2
|
+
import { COMPOSER_PLAN_FIELDS } from "./model-constants.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generated-plan state on composer nodes that a preset must NEITHER capture NOR
|
|
6
|
+
* carry, and that the dropdown must CLEAR when applying a preset:
|
|
7
|
+
*
|
|
8
|
+
* - every composer node's plan field (`COMPOSER_PLAN_MAP[*].planField` —
|
|
9
|
+
* sceneGraph / effectPlan / overlayPlan / titlePlan / motionPlan /
|
|
10
|
+
* compositePlan): a large LLM-authored plan blob, regenerated per run.
|
|
11
|
+
* - `lottieUrl`: the R2 url of the authored Lottie JSON (motion-graphics'
|
|
12
|
+
* `lottie` handle output) — a reapable generated artifact, not config.
|
|
13
|
+
*
|
|
14
|
+
* Derived from the single source of truth (COMPOSER_PLAN_MAP) so a new composer
|
|
15
|
+
* node type is covered by construction — no per-type list to keep in sync.
|
|
16
|
+
*
|
|
17
|
+
* Two distinct hazards this closes (see motion-graphics-presets design §2a/§2b):
|
|
18
|
+
* 1. Apply-time staleness: applying a preset over a node that already generated
|
|
19
|
+
* a plan would leave the OLD animation showing under the NEW prompt (the
|
|
20
|
+
* plan field is named by no patch, so it persists in the preview and on the
|
|
21
|
+
* output handle). The dropdown spreads `{ ...clearPatch, ...preset.data }`
|
|
22
|
+
* where clearPatch maps each key here to `undefined`.
|
|
23
|
+
* 2. Capture pollution: a user-saved preset would otherwise capture the plan
|
|
24
|
+
* blob (~tens of KB) + a stale url, re-injecting them on every apply and
|
|
25
|
+
* bloating the preset row. These keys are in the capture-exclusion set below.
|
|
26
|
+
*/
|
|
27
|
+
export const PRESET_APPLY_CLEAR_KEYS: readonly string[] = [...COMPOSER_PLAN_FIELDS, "lottieUrl"]
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Config fields that are intentionally NOT part of a preset, because they are not portable
|
|
31
|
+
* configuration — they identify the node on the canvas, wire it into one specific graph, or point
|
|
32
|
+
* at specific DB entities. Capturing them would make a preset silently wrong when applied to a
|
|
33
|
+
* different node or workflow. This is the single place that defines that contract, so the system
|
|
34
|
+
* stays correct-by-construction as new node types are added.
|
|
35
|
+
*
|
|
36
|
+
* Identity:
|
|
37
|
+
* - `label`: the node's canvas identity. Presets carry their own name and must not rename the node.
|
|
38
|
+
*
|
|
39
|
+
* Graph wiring (reference specific upstream node / handle / tile ids — meaningless elsewhere):
|
|
40
|
+
* - `fieldMappings`: field -> upstream node id.
|
|
41
|
+
* - `referenceImageOrder` / `referenceOrder` / `connectedMediaOrder` / `connectedRefImageOrder`:
|
|
42
|
+
* orderings keyed by the ids of wired upstream references.
|
|
43
|
+
*
|
|
44
|
+
* Structural identifiers (handle / port / route / channel ids that must stay unique per node and
|
|
45
|
+
* that downstream edges target — copying them across nodes orphans edges or duplicates a channel):
|
|
46
|
+
* - `routes` / `routeId` / `routeIds`: router routes + sub-workflow/condition route pairing ids.
|
|
47
|
+
* - `ports` / `inputPorts` / `outputPorts`: sub-workflow port arrays (each port has a handle id).
|
|
48
|
+
* - `channel` / `channelColor`: teleport send/receive pairing channel (auto-assigned per node).
|
|
49
|
+
*
|
|
50
|
+
* DB entity references / per-wiring identity state (tied to specific characters/locations):
|
|
51
|
+
* - `characterDefinitionIds`, `suppressedCanonicalCharacterIds`, `suppressedCanonicalLocationIds`.
|
|
52
|
+
* - `identityMeta`: per-identity overrides keyed by the wired identity's index/label.
|
|
53
|
+
* - `extraRefs`: extra references carrying `characterSlug` + workflow-specific asset urls.
|
|
54
|
+
*
|
|
55
|
+
* Preset meta:
|
|
56
|
+
* - `__activePresetId`: which preset is currently loaded onto the node (for the dropdown's
|
|
57
|
+
* active-name + dirty `*`). Node-local UI state, never part of a preset's own config.
|
|
58
|
+
* - `__promptFinalView`: field keys currently showing the final-prompt view (Edit ⇄ Final
|
|
59
|
+
* per-field toggle). Node-local UI state, never part of a preset's own config.
|
|
60
|
+
*
|
|
61
|
+
* NOTE: manual reference image urls (`referenceImageUrl` / `referenceImageUrls`) are deliberately
|
|
62
|
+
* KEPT — they are self-contained R2 urls a user set as input, and portable across nodes.
|
|
63
|
+
*
|
|
64
|
+
* Generated composer-plan state (`PRESET_APPLY_CLEAR_KEYS` — every COMPOSER_PLAN_MAP plan field +
|
|
65
|
+
* `lottieUrl`): NOT portable config — a per-run LLM-authored plan blob / reapable artifact url.
|
|
66
|
+
* Excluded here (preset-scoped, zero blast radius) rather than via EXECUTION_DATA_KEYS, whose
|
|
67
|
+
* non-preset consumers (undo-skip in use-workflow-store, auto-execute IGNORE_KEYS) would then
|
|
68
|
+
* treat a legitimately-persisted plan as transient runtime state.
|
|
69
|
+
*/
|
|
70
|
+
export const PRESET_EXCLUDED_KEYS: ReadonlySet<string> = new Set([
|
|
71
|
+
"label",
|
|
72
|
+
"fieldMappings",
|
|
73
|
+
"__activePresetId",
|
|
74
|
+
"__promptFinalView",
|
|
75
|
+
"referenceImageOrder",
|
|
76
|
+
"referenceOrder",
|
|
77
|
+
"connectedMediaOrder",
|
|
78
|
+
"connectedRefImageOrder",
|
|
79
|
+
"characterDefinitionIds",
|
|
80
|
+
"suppressedCanonicalCharacterIds",
|
|
81
|
+
"suppressedCanonicalLocationIds",
|
|
82
|
+
"identityMeta",
|
|
83
|
+
"extraRefs",
|
|
84
|
+
// structural identifiers
|
|
85
|
+
"routes",
|
|
86
|
+
"routeId",
|
|
87
|
+
"routeIds",
|
|
88
|
+
"ports",
|
|
89
|
+
"inputPorts",
|
|
90
|
+
"outputPorts",
|
|
91
|
+
"channel",
|
|
92
|
+
"channelColor",
|
|
93
|
+
// generated composer-plan state (derived from COMPOSER_PLAN_MAP + lottieUrl)
|
|
94
|
+
...PRESET_APPLY_CLEAR_KEYS,
|
|
95
|
+
])
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Capture a node's reusable configuration from its `data`: a shallow copy minus runtime/result
|
|
99
|
+
* state (EXECUTION_DATA_KEYS) and graph/identity fields (PRESET_EXCLUDED_KEYS). Pure; never mutates
|
|
100
|
+
* the input. Node-type-agnostic — the node type is stored alongside the result by the caller.
|
|
101
|
+
*/
|
|
102
|
+
export function extractPresetData(
|
|
103
|
+
data: Readonly<Record<string, unknown>>,
|
|
104
|
+
): Record<string, unknown> {
|
|
105
|
+
const out: Record<string, unknown> = {}
|
|
106
|
+
for (const key of Object.keys(data)) {
|
|
107
|
+
if (EXECUTION_DATA_KEYS.has(key)) continue
|
|
108
|
+
if (PRESET_EXCLUDED_KEYS.has(key)) continue
|
|
109
|
+
out[key] = data[key]
|
|
110
|
+
}
|
|
111
|
+
return out
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function deepEqual(a: unknown, b: unknown): boolean {
|
|
115
|
+
if (a === b) return true
|
|
116
|
+
if (typeof a !== typeof b) return false
|
|
117
|
+
if (a === null || b === null) return a === b
|
|
118
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
119
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false
|
|
120
|
+
return a.every((v, i) => deepEqual(v, b[i]))
|
|
121
|
+
}
|
|
122
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
123
|
+
const ka = Object.keys(a as object)
|
|
124
|
+
const kb = Object.keys(b as object)
|
|
125
|
+
if (ka.length !== kb.length) return false
|
|
126
|
+
return ka.every((k) =>
|
|
127
|
+
deepEqual((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* True iff the node's `data` still matches the preset — i.e. every key the preset defines
|
|
135
|
+
* deep-equals the node's current value. Compares only preset-defined keys (the node may carry
|
|
136
|
+
* extra keys the preset doesn't set, e.g. seed, which must NOT count as a mismatch). Used to drive
|
|
137
|
+
* the dropdown's dirty `*` indicator.
|
|
138
|
+
*/
|
|
139
|
+
export function presetDataMatches(
|
|
140
|
+
nodeData: Readonly<Record<string, unknown>>,
|
|
141
|
+
presetData: Readonly<Record<string, unknown>>,
|
|
142
|
+
): boolean {
|
|
143
|
+
for (const key of Object.keys(presetData)) {
|
|
144
|
+
if (!deepEqual(nodeData[key], presetData[key])) return false
|
|
145
|
+
}
|
|
146
|
+
return true
|
|
147
|
+
}
|
package/src/node-refs.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node reference pattern and resolution for {Node Label} template syntax.
|
|
3
|
+
* Shared between frontend and backend.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Matches {Node Label} references in text. */
|
|
7
|
+
export const NODE_REF_PATTERN = /\{([^}]+)\}/g
|
|
8
|
+
|
|
9
|
+
/** Reserved template variable names that should NOT be resolved as node refs */
|
|
10
|
+
export const RESERVED_TEMPLATE_VARS = new Set([
|
|
11
|
+
"name",
|
|
12
|
+
"description",
|
|
13
|
+
"userPrompt",
|
|
14
|
+
"assetDescriptions",
|
|
15
|
+
"outputCount",
|
|
16
|
+
])
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Split a `{...}` token body into its node name and optional fallback. Splits on the FIRST `||`
|
|
20
|
+
* (a fallback may itself contain `||`); both sides are trimmed. `fallback` is null when there is
|
|
21
|
+
* no `||`, preserving the legacy "leave the literal token" behavior.
|
|
22
|
+
* "person || man" -> { name: "person", fallback: "man" }
|
|
23
|
+
* "person || " -> { name: "person", fallback: "" } (resolves to empty when absent)
|
|
24
|
+
* "person" -> { name: "person", fallback: null } (literal {person} when absent)
|
|
25
|
+
*/
|
|
26
|
+
export function parseNodeRef(raw: string): { name: string; fallback: string | null } {
|
|
27
|
+
const i = raw.indexOf("||")
|
|
28
|
+
if (i === -1) return { name: raw.trim(), fallback: null }
|
|
29
|
+
return { name: raw.slice(0, i).trim(), fallback: raw.slice(i + 2).trim() }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Canonical form of a node-name variable: trimmed + lowercased. Node-name
|
|
34
|
+
* variables are CASE-INSENSITIVE with lowercase as canonical, so nodes labeled
|
|
35
|
+
* `TEXt`, `TEXT`, `text` all feed the one `{text}` variable and `{TEXT}` /
|
|
36
|
+
* `{Text}` / `{text}` in a prompt all resolve to it.
|
|
37
|
+
*
|
|
38
|
+
* Reserved template vars (RESERVED_TEMPLATE_VARS, e.g. `userPrompt`) are matched
|
|
39
|
+
* case-sensitively on the ORIGINAL token BEFORE canonicalization, so `{userPrompt}`
|
|
40
|
+
* stays a reserved token rather than becoming a `userprompt` node ref.
|
|
41
|
+
*/
|
|
42
|
+
export function canonicalVarName(label: string): string {
|
|
43
|
+
return label.trim().toLowerCase()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The distinct `{label}` names referenced across the given texts. Mirrors the
|
|
48
|
+
* canvas-side `referencedRefs`: strips the `|| fallback`, and ignores empty
|
|
49
|
+
* tokens, image-ref tokens (`{image:N}`), and RESERVED_TEMPLATE_VARS. Used to
|
|
50
|
+
* SUPPRESS auto-injection of a connected node the author already placed
|
|
51
|
+
* explicitly via `{label}` (so it isn't injected twice). `matchAll` over the
|
|
52
|
+
* global pattern does not mutate its lastIndex, so sharing the constant is safe.
|
|
53
|
+
*/
|
|
54
|
+
export function extractReferencedLabels(
|
|
55
|
+
...texts: ReadonlyArray<string | undefined | null>
|
|
56
|
+
): Set<string> {
|
|
57
|
+
const refs = new Set<string>()
|
|
58
|
+
for (const text of texts) {
|
|
59
|
+
if (typeof text !== "string" || text.length === 0) continue
|
|
60
|
+
for (const m of text.matchAll(NODE_REF_PATTERN)) {
|
|
61
|
+
const { name } = parseNodeRef(m[1] ?? "")
|
|
62
|
+
if (name === "" || name.startsWith("image:") || RESERVED_TEMPLATE_VARS.has(name)) continue
|
|
63
|
+
refs.add(canonicalVarName(name)) // case-insensitive: {Foo} suppresses a `foo` node
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return refs
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Handle → combine-order category for same-label `{label}` merging. Lower wins
|
|
70
|
+
* first: prompt (0) → elements (1) → look family (2). Other handles are -1
|
|
71
|
+
* (not combinable — they keep the per-engine last-wins/suffix behavior). */
|
|
72
|
+
export const REF_HANDLE_CATEGORY: Readonly<Record<string, number>> = {
|
|
73
|
+
prompt: 0,
|
|
74
|
+
elements: 1,
|
|
75
|
+
look: 2,
|
|
76
|
+
cinematography: 2,
|
|
77
|
+
style: 2,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Combine-order category for a direct-parent edge's `targetHandle`; -1 when the
|
|
81
|
+
* handle isn't one of the combinable prompt/elements/look handles. */
|
|
82
|
+
export function refHandleCategory(targetHandle: string | null | undefined): number {
|
|
83
|
+
if (targetHandle != null && Object.prototype.hasOwnProperty.call(REF_HANDLE_CATEGORY, targetHandle)) {
|
|
84
|
+
return REF_HANDLE_CATEGORY[targetHandle]
|
|
85
|
+
}
|
|
86
|
+
return -1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reference-handle id → resolved-input key. The SINGLE SOURCE OF TRUTH for which
|
|
91
|
+
* `targetHandle` ids carry image / video / audio reference inputs. Shared by the
|
|
92
|
+
* backend input-resolver (routes the wired URL into the matching
|
|
93
|
+
* `referenceImageUrls` / `referenceVideoUrls` / `referenceAudioUrls` slot) AND
|
|
94
|
+
* the positional `{image:N}` / `{video:N}` / `{audio:N}` body-token COUNTS in the
|
|
95
|
+
* FE preview (`video-prompt-assembly.ts`), FE run (`execute-node.ts`), and BE
|
|
96
|
+
* payload-builder.
|
|
97
|
+
*
|
|
98
|
+
* Two handle families map to the same keys:
|
|
99
|
+
* - Legacy / i2v single-name ids (`references` / `reference-videos` /
|
|
100
|
+
* `reference-audio`) — kept for un-migrated workflows.
|
|
101
|
+
* - Canonical typed-handle ids the modern Generate Video node exposes
|
|
102
|
+
* (`imageReferences` / `videoReferences` / `audioReferences`) — share the
|
|
103
|
+
* resolved-input keys with the legacy ids so consumer code never forks.
|
|
104
|
+
*/
|
|
105
|
+
export const REFERENCE_HANDLE_MAP: Record<string, "referenceImageUrls" | "referenceVideoUrls" | "referenceAudioUrls"> = {
|
|
106
|
+
// Legacy / i2v single-name handle ids (kept for un-migrated workflows)
|
|
107
|
+
"references": "referenceImageUrls",
|
|
108
|
+
"reference-videos": "referenceVideoUrls",
|
|
109
|
+
"reference-audio": "referenceAudioUrls",
|
|
110
|
+
// New canonical typed-handle ids (Generate Video) — share the resolved-input
|
|
111
|
+
// keys with the legacy ids so payload-builder code doesn't fork.
|
|
112
|
+
"imageReferences": "referenceImageUrls",
|
|
113
|
+
"videoReferences": "referenceVideoUrls",
|
|
114
|
+
"audioReferences": "referenceAudioUrls",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The three reference modalities a reference handle can carry. */
|
|
118
|
+
export type ReferenceModality = "image" | "video" | "audio"
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The reference MODALITY a `targetHandle` carries, or null when the handle is
|
|
122
|
+
* not a reference handle. Resolves BOTH the legacy single-name ids and the
|
|
123
|
+
* canonical Generate Video ids (via REFERENCE_HANDLE_MAP), so positional
|
|
124
|
+
* `{image:N}` / `{video:N}` / `{audio:N}` token counts cover every handle alias
|
|
125
|
+
* — without this, the generate-video `imageReferences` handle was silently
|
|
126
|
+
* uncounted and `{image:N}` tokens dropped to bare labels on the only creatable
|
|
127
|
+
* video node.
|
|
128
|
+
*/
|
|
129
|
+
export function referenceModalityForHandle(targetHandle: string | null | undefined): ReferenceModality | null {
|
|
130
|
+
if (targetHandle == null) return null
|
|
131
|
+
const key = REFERENCE_HANDLE_MAP[targetHandle]
|
|
132
|
+
return key === "referenceImageUrls" ? "image" : key === "referenceVideoUrls" ? "video" : key === "referenceAudioUrls" ? "audio" : null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Frame target handles (start / end keyframes). An image wired into one of these
|
|
137
|
+
* is a KEYFRAME, not an `{image:N}` reference: the backend appends frames at the
|
|
138
|
+
* TAIL of `reference_image_urls` (`resolveSeedance2Inputs`), never numbered. The
|
|
139
|
+
* editor MUST therefore exclude them from `{image:N}` numbering so editor token N
|
|
140
|
+
* equals the worker reference slot N.
|
|
141
|
+
*
|
|
142
|
+
* The SINGLE source of truth for "is this a frame handle," shared by the
|
|
143
|
+
* config-panel autocomplete (`video-configs.tsx`) AND the inline/modal builder
|
|
144
|
+
* (`connected-references.ts`) — without ONE definition those two surfaces drifted
|
|
145
|
+
* and the inline editor numbered the frame as `{image:1}` (the C1 bug). Frames
|
|
146
|
+
* are deliberately NOT in `REFERENCE_HANDLE_MAP`: they carry no reference
|
|
147
|
+
* modality, so `referenceModalityForHandle` already returns `null` for them on
|
|
148
|
+
* the count side — this set is the exclusion lever for the EDITOR-numbering side.
|
|
149
|
+
*/
|
|
150
|
+
export const FRAME_TARGET_HANDLES: ReadonlySet<string> = new Set(["startFrame", "endFrame"])
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Minimal structural edge shape {@link countRefModalityEdges} reads — only the
|
|
154
|
+
* two fields the count needs. Both the FE `WorkflowEdge` (xyflow `Edge`, whose
|
|
155
|
+
* `target` is a non-null string) and the BE `SimpleEdge` satisfy it.
|
|
156
|
+
*/
|
|
157
|
+
export interface RefModalityEdge {
|
|
158
|
+
target?: string | null
|
|
159
|
+
targetHandle?: string | null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Count the reference-handle edges of a given reference MODALITY wired into a
|
|
164
|
+
* node — the ONE positional `{image:N}` / `{video:N}` / `{audio:N}` counter
|
|
165
|
+
* shared by the FE preview (`video-prompt-assembly.ts`), the FE run
|
|
166
|
+
* (`execute-node.ts`), and the BE orchestrator (`payload-builder.ts`). Counting
|
|
167
|
+
* via `referenceModalityForHandle` (not a single handle string) covers BOTH the
|
|
168
|
+
* legacy single-name ids (`references` / `reference-videos` / `reference-audio`)
|
|
169
|
+
* AND the canonical Generate Video ids (`imageReferences` / `videoReferences` /
|
|
170
|
+
* `audioReferences`), so editor token N maps 1:1 to the worker
|
|
171
|
+
* `referenceImageUrls` / `referenceVideoUrls` / `referenceAudioUrls` slot N on
|
|
172
|
+
* every handle alias. This is the EDGE count (not the resolved/list-expanded URL
|
|
173
|
+
* count) so all three paths stay byte-identical; the edge-vs-resolved divergence
|
|
174
|
+
* for a reference edge that fans out to >1 URL (a List producer) is a shared,
|
|
175
|
+
* documented v1 limitation.
|
|
176
|
+
*/
|
|
177
|
+
export function countRefModalityEdges(
|
|
178
|
+
edges: ReadonlyArray<RefModalityEdge>,
|
|
179
|
+
nodeId: string,
|
|
180
|
+
modality: ReferenceModality,
|
|
181
|
+
): number {
|
|
182
|
+
let n = 0
|
|
183
|
+
for (const e of edges) {
|
|
184
|
+
if (e.target === nodeId && referenceModalityForHandle(e.targetHandle) === modality) n += 1
|
|
185
|
+
}
|
|
186
|
+
return n
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** A candidate contribution to the `{label}` map: the node's label, its resolved
|
|
190
|
+
* output, and its direct-parent handle category (-1 = not combinable / deeper). */
|
|
191
|
+
export interface RefCandidate {
|
|
192
|
+
label: string
|
|
193
|
+
output: string
|
|
194
|
+
category: number
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Same-label COMBINE: when ≥2 direct-parent candidates (category ≥ 0) share a
|
|
199
|
+
* label, `{label}` resolves to their values joined by ", " ordered prompt →
|
|
200
|
+
* elements → look, and within a category by candidate (edge) order. Returns a
|
|
201
|
+
* Map of ONLY the combined labels; the caller fills the rest with its own
|
|
202
|
+
* (FE suffix / BE last-wins) logic, skipping any label present here. Shared so
|
|
203
|
+
* the frontend executor and backend orchestrator never drift.
|
|
204
|
+
*/
|
|
205
|
+
export function combineSameLabelRefs(candidates: ReadonlyArray<RefCandidate>): Map<string, string> {
|
|
206
|
+
const byLabel = new Map<string, RefCandidate[]>()
|
|
207
|
+
for (const c of candidates) {
|
|
208
|
+
if (c.category < 0) continue
|
|
209
|
+
const key = canonicalVarName(c.label) // TEXt + TEXT + text collapse into one {text}
|
|
210
|
+
const arr = byLabel.get(key)
|
|
211
|
+
if (arr) arr.push(c)
|
|
212
|
+
else byLabel.set(key, [c])
|
|
213
|
+
}
|
|
214
|
+
const combined = new Map<string, string>()
|
|
215
|
+
for (const [label, group] of byLabel) {
|
|
216
|
+
if (group.length < 2) continue
|
|
217
|
+
const ordered = group
|
|
218
|
+
.map((c, i) => ({ c, i }))
|
|
219
|
+
.sort((a, b) => a.c.category - b.c.category || a.i - b.i)
|
|
220
|
+
.map((x) => x.c.output)
|
|
221
|
+
combined.set(label, ordered.join(", "))
|
|
222
|
+
}
|
|
223
|
+
return combined
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Resolve {Node Label} references in text by replacing them with actual node outputs.
|
|
228
|
+
* Skips reserved template variables used by applyTemplate().
|
|
229
|
+
* Iterates until stable to handle nested refs (e.g. {List} → {Animal1} → "dog").
|
|
230
|
+
*/
|
|
231
|
+
export function resolveNodeRefs(
|
|
232
|
+
text: string,
|
|
233
|
+
labelToOutput: ReadonlyMap<string, string>,
|
|
234
|
+
): string {
|
|
235
|
+
const MAX_PASSES = 10
|
|
236
|
+
let result = text
|
|
237
|
+
for (let i = 0; i < MAX_PASSES; i++) {
|
|
238
|
+
const next = result.replace(NODE_REF_PATTERN, (match, raw: string) => {
|
|
239
|
+
const { name, fallback } = parseNodeRef(raw)
|
|
240
|
+
if (RESERVED_TEMPLATE_VARS.has(name)) return match
|
|
241
|
+
// Node-name vars are case-insensitive: try the literal key first (keeps
|
|
242
|
+
// original-cased maps like condition-variables working), then the
|
|
243
|
+
// lowercase-canonical key (node-ref maps are canonical-keyed).
|
|
244
|
+
const output = labelToOutput.get(name) ?? labelToOutput.get(canonicalVarName(name))
|
|
245
|
+
if (output !== undefined) return output // connected, non-empty output → its value
|
|
246
|
+
if (fallback !== null) return fallback // absent/empty + fallback → default ("" for {name || })
|
|
247
|
+
return match // absent + no || → literal {name}
|
|
248
|
+
})
|
|
249
|
+
if (next === result) break
|
|
250
|
+
result = next
|
|
251
|
+
}
|
|
252
|
+
return result
|
|
253
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field names on a node's `data` that hold runtime / result / transient execution
|
|
3
|
+
* state (job ids, progress, generated outputs, fan-out bookkeeping). These are NEVER
|
|
4
|
+
* part of a node's reusable configuration.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth, consumed by:
|
|
7
|
+
* - the workflow store's undo logic (execution-only updates skip undo capture, so job
|
|
8
|
+
* polling does not pollute undo history or flip `isDirty`)
|
|
9
|
+
* - the node-preset extractor (presets must never capture runtime state)
|
|
10
|
+
* - the backend defensive strip on preset writes
|
|
11
|
+
*
|
|
12
|
+
* When adding a new runtime/result field to a node's data, add its key here.
|
|
13
|
+
*/
|
|
14
|
+
export const EXECUTION_DATA_KEYS: ReadonlySet<string> = new Set([
|
|
15
|
+
"executionStatus",
|
|
16
|
+
"currentJobId",
|
|
17
|
+
"currentJobProgress",
|
|
18
|
+
"errorMessage",
|
|
19
|
+
"isStreaming",
|
|
20
|
+
"generatedImageUrl",
|
|
21
|
+
"generatedVideoUrl",
|
|
22
|
+
"generatedAudioUrl",
|
|
23
|
+
"generatedText",
|
|
24
|
+
"generatedScript",
|
|
25
|
+
"generatedItems",
|
|
26
|
+
"generatedResults",
|
|
27
|
+
"activeResultIndex",
|
|
28
|
+
"sourceImageUrl",
|
|
29
|
+
"__listTotal",
|
|
30
|
+
"__listCompleted",
|
|
31
|
+
"__listResults",
|
|
32
|
+
// List fan-out window flag (abandon-guard exemption). Set/cleared by
|
|
33
|
+
// executeNodeForList — purely execution-related, never user-edited.
|
|
34
|
+
"__listRunning",
|
|
35
|
+
// Selector node dual-channel outputs (picked + rest). Server-side execution
|
|
36
|
+
// output, not user-edited config.
|
|
37
|
+
"pickedResults",
|
|
38
|
+
"restResults",
|
|
39
|
+
"__pickedResults",
|
|
40
|
+
"__restResults",
|
|
41
|
+
"__pickedTotal",
|
|
42
|
+
"__restTotal",
|
|
43
|
+
"generatedJson",
|
|
44
|
+
"subWorkflowProgress",
|
|
45
|
+
"outputResults",
|
|
46
|
+
"shots",
|
|
47
|
+
"result",
|
|
48
|
+
"processedResult",
|
|
49
|
+
"activeRoutes",
|
|
50
|
+
"routeOutputs",
|
|
51
|
+
"_upstreamRefresh",
|
|
52
|
+
"zoom",
|
|
53
|
+
// Character LoRA training status fields — written every 8s while training.
|
|
54
|
+
"loraReplicateVersion",
|
|
55
|
+
"loraTriggerWord",
|
|
56
|
+
"loraTrainingStatus",
|
|
57
|
+
// Collect (fan-in) execution snapshot.
|
|
58
|
+
"lastInputs",
|
|
59
|
+
"lastMeta",
|
|
60
|
+
"__upstreamCount",
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The PURE RUN-STATE subset of EXECUTION_DATA_KEYS: per-tick values (status
|
|
65
|
+
* flips, job ids, progress counters, fan-out bookkeeping) that must NEITHER
|
|
66
|
+
* mark the workflow dirty NOR be persisted in the save payload. Everything
|
|
67
|
+
* else in EXECUTION_DATA_KEYS is a RESULT the user expects to survive reload
|
|
68
|
+
* (generated URLs/results, errorMessage, LoRA outputs, collect snapshots).
|
|
69
|
+
*
|
|
70
|
+
* Why this split exists: writing these keys used to set `isDirty`, so job
|
|
71
|
+
* polling phantom-dirtied passive tabs → spurious autosaves → false
|
|
72
|
+
* "changed in another tab" banners → the remote-ahead latch froze autosave
|
|
73
|
+
* (the "not saved for a long time" report). Consumed by the workflow
|
|
74
|
+
* store's dirty decision and the save-payload sanitizer below.
|
|
75
|
+
*
|
|
76
|
+
* Invariant (guarded by node-runtime-keys.test.ts): subset of
|
|
77
|
+
* EXECUTION_DATA_KEYS — anything transient is also undo-exempt.
|
|
78
|
+
*/
|
|
79
|
+
export const TRANSIENT_RUNTIME_KEYS: ReadonlySet<string> = new Set([
|
|
80
|
+
"executionStatus",
|
|
81
|
+
"currentJobId",
|
|
82
|
+
"currentJobProgress",
|
|
83
|
+
"isStreaming",
|
|
84
|
+
"subWorkflowProgress",
|
|
85
|
+
"__listTotal",
|
|
86
|
+
"__listCompleted",
|
|
87
|
+
"__listRunning",
|
|
88
|
+
"_upstreamRefresh",
|
|
89
|
+
"__upstreamCount",
|
|
90
|
+
])
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Pure save-payload sanitizer: returns new node objects with the transient
|
|
94
|
+
* run-state keys removed from `data`. Nodes without transient keys (or
|
|
95
|
+
* without `data`) are returned by reference — cheap for the common case.
|
|
96
|
+
*/
|
|
97
|
+
export function stripTransientRuntimeData<
|
|
98
|
+
T extends { data?: Record<string, unknown> | undefined },
|
|
99
|
+
>(nodes: readonly T[]): T[] {
|
|
100
|
+
return nodes.map((node) => {
|
|
101
|
+
const data = node.data
|
|
102
|
+
if (!data) return node
|
|
103
|
+
let hasTransient = false
|
|
104
|
+
for (const key of TRANSIENT_RUNTIME_KEYS) {
|
|
105
|
+
if (key in data) {
|
|
106
|
+
hasTransient = true
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!hasTransient) return node
|
|
111
|
+
const cleaned: Record<string, unknown> = {}
|
|
112
|
+
for (const [key, value] of Object.entries(data)) {
|
|
113
|
+
if (!TRANSIENT_RUNTIME_KEYS.has(key)) cleaned[key] = value
|
|
114
|
+
}
|
|
115
|
+
return { ...node, data: cleaned }
|
|
116
|
+
})
|
|
117
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Object aspect-ratio defaults
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// Objects are isolated subjects — product-showcase framing. Unlike characters
|
|
6
|
+
// (which have per-asset-type framing differences — head shots 3:4, full-body
|
|
7
|
+
// 9:16), objects use 1:1 square framing for every asset type by default. This
|
|
8
|
+
// matches the natural framing of the Object Studio's asset-card grid (square
|
|
9
|
+
// tiles) and avoids cropping the subject regardless of which variant axis
|
|
10
|
+
// (angles/materials/variations/motion) is being generated.
|
|
11
|
+
//
|
|
12
|
+
// Resolution precedence (low → high):
|
|
13
|
+
// 1. Per-asset-type default (this table — all 1:1 currently)
|
|
14
|
+
// 2. Object node's `defaultAssetAspectRatio` (user override, Phase E)
|
|
15
|
+
// 3. Explicit `aspectRatio` on the API request (caller wins)
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export const OBJECT_ASPECT_OPTIONS = ["1:1", "3:4", "16:9", "9:16", "4:3"] as const
|
|
19
|
+
export type ObjectAspectRatio = (typeof OBJECT_ASPECT_OPTIONS)[number]
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The asset categories with distinct natural framing. For objects, all bucket
|
|
23
|
+
* defaults are currently `"1:1"`, but the per-asset-type table is kept (vs a
|
|
24
|
+
* single flat constant) so future tuning (e.g., wider framing for motion clips
|
|
25
|
+
* with camera-orbit moves) can be added without touching the resolver.
|
|
26
|
+
*/
|
|
27
|
+
export type ObjectAssetTypeForAspect = "angles" | "materials" | "variations" | "motion" | "custom"
|
|
28
|
+
|
|
29
|
+
export const OBJECT_ASPECT_DEFAULTS: Record<ObjectAssetTypeForAspect, ObjectAspectRatio> = {
|
|
30
|
+
angles: "1:1",
|
|
31
|
+
materials: "1:1",
|
|
32
|
+
variations: "1:1",
|
|
33
|
+
// Motion clips are 1:1 because objects rotate in place. Switch to "16:9" if
|
|
34
|
+
// future motion variants pan through a scene (e.g., drone-orbit with horizon).
|
|
35
|
+
motion: "1:1",
|
|
36
|
+
custom: "1:1",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isObjectAspectRatio(s: string): s is ObjectAspectRatio {
|
|
40
|
+
return (OBJECT_ASPECT_OPTIONS as readonly string[]).includes(s)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ResolveObjectAspectOptions {
|
|
44
|
+
/** Explicit `aspectRatio` on the request body — highest precedence. */
|
|
45
|
+
readonly explicit?: string | null
|
|
46
|
+
/**
|
|
47
|
+
* The object node's `defaultAssetAspectRatio` — set per-canvas-node via the
|
|
48
|
+
* 5-pill toggle on the object node (Phase E). Flows through the API as
|
|
49
|
+
* `objectNodeAspectRatio`.
|
|
50
|
+
*/
|
|
51
|
+
readonly nodeOverride?: string | null
|
|
52
|
+
/** What's being generated. Drives the default when nothing else is set. */
|
|
53
|
+
readonly assetType: ObjectAssetTypeForAspect
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the final aspect ratio for an object generation request.
|
|
58
|
+
*
|
|
59
|
+
* Precedence: explicit > node override > per-asset-type default. The two
|
|
60
|
+
* "soft" inputs are tolerated as raw strings and only honored when they parse
|
|
61
|
+
* to a value in OBJECT_ASPECT_OPTIONS — anything else falls through to the
|
|
62
|
+
* next layer (so a stale enum value doesn't poison the generation).
|
|
63
|
+
*/
|
|
64
|
+
export function resolveObjectAspectRatio(opts: ResolveObjectAspectOptions): ObjectAspectRatio {
|
|
65
|
+
if (opts.explicit && isObjectAspectRatio(opts.explicit)) {
|
|
66
|
+
return opts.explicit
|
|
67
|
+
}
|
|
68
|
+
if (opts.nodeOverride && isObjectAspectRatio(opts.nodeOverride)) {
|
|
69
|
+
return opts.nodeOverride
|
|
70
|
+
}
|
|
71
|
+
return OBJECT_ASPECT_DEFAULTS[opts.assetType]
|
|
72
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PARAMETER_NODE_TYPES } from "./parameter-node-value.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The 5 parameter-picker node types that can wire INTO the Object Studio's
|
|
5
|
+
* `type` input handle as upstream identity providers (Material, Animal,
|
|
6
|
+
* Vehicle, Weapon, Furniture). The resolver in
|
|
7
|
+
* `backend/src/services/workflow-engine/input-resolver.ts` (and its frontend
|
|
8
|
+
* mirror) reads this set to discriminate wired pickers from arbitrary source
|
|
9
|
+
* nodes when composing the seedPromptHint.
|
|
10
|
+
*
|
|
11
|
+
* Defensive filter: if `PARAMETER_NODE_TYPES` doesn't yet contain `"furniture"`
|
|
12
|
+
* (i.e., the Furniture prereq PR hasn't been deployed in this environment),
|
|
13
|
+
* furniture silently drops from the set. The wired-picker resolver then
|
|
14
|
+
* skips furniture pickers without error rather than firing a wrong prompt
|
|
15
|
+
* fragment. Production deploy order is enforced as: Prereq PR (#2601, merged)
|
|
16
|
+
* → Object Studio PRs; this defensive code is a safety net.
|
|
17
|
+
*/
|
|
18
|
+
export const OBJECT_PICKER_NODE_TYPES: ReadonlySet<string> = new Set(
|
|
19
|
+
["material", "animal", "vehicle", "weapon", "furniture"].filter((t) =>
|
|
20
|
+
PARAMETER_NODE_TYPES.has(t),
|
|
21
|
+
),
|
|
22
|
+
)
|