@vectoriox/iox-ui 5.78.0 → 5.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { FrameSequence } from '@vectoriox/iox-ui/media-core';
1
2
  import { AnimateId, DesignChoices, DesignTokenLike } from '@vectoriox/iox-ui/design-core';
2
3
  import { SeoRawDoc } from '@vectoriox/iox-ui/seo-core';
3
4
  import { ContentFieldLike } from '@vectoriox/iox-ui/composite-core';
@@ -13,6 +14,10 @@ declare const SPEC_SITE_PLAN_SHAPE: {
13
14
  readonly dataSources: "(cmsDataSource | relativeDataSource)[] — merged by alias into the org library";
14
15
  readonly pages: "page[]";
15
16
  readonly globals: "globals";
17
+ readonly "film?": "film — the owner's film as pictures (SC6b), set by the build and placed by placeFilm; never a model's";
18
+ };
19
+ readonly film: {
20
+ readonly sequence: "object — a media-core FrameSequence: where the numbered pictures live, how many, their size";
16
21
  };
17
22
  readonly sourceTemplate: {
18
23
  readonly kind: "'template'";
@@ -399,6 +404,104 @@ declare const SPEC_SITE_PATTERNS: {
399
404
  }];
400
405
  };
401
406
  };
407
+ readonly "hero.scene-object": {
408
+ readonly family: "hero";
409
+ readonly scene: true;
410
+ readonly film: true;
411
+ readonly label: "Hero — your film plays as you scroll";
412
+ readonly intent: "The studio's own film plays forward and back with the scroll behind the headline while the page holds still.";
413
+ readonly node: {
414
+ readonly t: "Section";
415
+ readonly es: "section";
416
+ readonly scheme: "inverse";
417
+ readonly role: "scene";
418
+ readonly lay: {
419
+ readonly position: "relative";
420
+ readonly display: "block";
421
+ readonly width: "100%";
422
+ readonly height: "300vh";
423
+ readonly padding: "0px";
424
+ };
425
+ readonly c: readonly [{
426
+ readonly t: "Container";
427
+ readonly role: "scene.stage";
428
+ readonly lay: {
429
+ readonly position: "sticky";
430
+ readonly top: "0px";
431
+ readonly display: "block";
432
+ readonly width: "100%";
433
+ readonly height: "100svh";
434
+ readonly overflow: "hidden";
435
+ readonly padding: "0px";
436
+ };
437
+ readonly c: readonly [{
438
+ readonly t: "Video";
439
+ readonly es: "media";
440
+ readonly film: true;
441
+ readonly lay: {
442
+ readonly position: "absolute";
443
+ readonly top: "0px";
444
+ readonly left: "0px";
445
+ readonly width: "100%";
446
+ readonly height: "100%";
447
+ readonly aspectRatio: "auto";
448
+ readonly borderRadius: "0px";
449
+ };
450
+ readonly tracks: readonly [{
451
+ readonly prop: "play";
452
+ readonly keys: readonly [readonly [0, 0], readonly [1, 100]];
453
+ }];
454
+ }, {
455
+ readonly t: "Container";
456
+ readonly es: "media-scrim";
457
+ readonly lay: {
458
+ readonly display: "flex";
459
+ readonly flexDirection: "column";
460
+ readonly gap: "14px";
461
+ readonly position: "absolute";
462
+ readonly left: "0px";
463
+ readonly bottom: "0px";
464
+ readonly width: "100%";
465
+ readonly padding: "48px 32px 56px 32px";
466
+ };
467
+ readonly c: readonly [{
468
+ readonly t: "Heading";
469
+ readonly es: "display";
470
+ readonly level: "h1";
471
+ readonly slot: "headline";
472
+ readonly max: 56;
473
+ readonly fromBusiness: "name";
474
+ readonly tracks: readonly [{
475
+ readonly prop: "y";
476
+ readonly keys: readonly [readonly [0, 0], readonly [1, -6]];
477
+ }];
478
+ }, {
479
+ readonly t: "Text";
480
+ readonly es: "lead";
481
+ readonly slot: "lead";
482
+ readonly max: 180;
483
+ readonly fromBusiness: "tagline";
484
+ readonly ph: "copy";
485
+ readonly tracks: readonly [{
486
+ readonly prop: "opacity";
487
+ readonly keys: readonly [readonly [0.3, 0], readonly [0.45, 1]];
488
+ }];
489
+ }, {
490
+ readonly t: "ButtonBlock";
491
+ readonly es: "button-primary";
492
+ readonly href: "/contact";
493
+ readonly slot: "cta";
494
+ readonly max: 24;
495
+ readonly text: "Get in touch";
496
+ readonly tracks: readonly [{
497
+ readonly prop: "opacity";
498
+ readonly keys: readonly [readonly [0.35, 0], readonly [0.5, 1]];
499
+ }];
500
+ }];
501
+ }];
502
+ }];
503
+ };
504
+ };
402
505
  readonly "story.scene-chapters": {
403
506
  readonly family: "story";
404
507
  readonly scene: true;
@@ -4523,6 +4626,10 @@ declare const SPEC_SITE_LIBRARY: readonly [{
4523
4626
  readonly id: "hero.scene-depth";
4524
4627
  readonly family: "hero";
4525
4628
  readonly composed: true;
4629
+ }, {
4630
+ readonly id: "hero.scene-object";
4631
+ readonly family: "hero";
4632
+ readonly composed: true;
4526
4633
  }, {
4527
4634
  readonly id: "proof.logo-strip";
4528
4635
  readonly family: "proof";
@@ -4887,6 +4994,12 @@ interface SitePlan {
4887
4994
  readonly dataSources: ReadonlyArray<DataSourcePlan>;
4888
4995
  readonly pages: ReadonlyArray<PagePlan>;
4889
4996
  readonly globals: Readonly<Record<`${GlobalSlot}`, string>>;
4997
+ /** SC6b: the owner's film as pictures, set by the build and placed by `placeFilm` — never a model's. */
4998
+ readonly film?: PlanFilm;
4999
+ }
5000
+ /** SC6b: the owner's film — the numbered pictures the wizard's browser made of their video (media-core). */
5001
+ interface PlanFilm {
5002
+ readonly sequence: FrameSequence;
4890
5003
  }
4891
5004
 
4892
5005
  /**
@@ -5098,6 +5211,8 @@ interface PatternNode {
5098
5211
  /** SC6: a Scene layer's scene-core tracks, written to the node's `sceneTracks` (and `scenePhoneTracks`). */
5099
5212
  readonly tracks?: ReadonlyArray<PatternTrack>;
5100
5213
  readonly phoneTracks?: ReadonlyArray<PatternTrack>;
5214
+ /** SC6b: the Video a film pattern plays the owner's film in (its `frames`). */
5215
+ readonly film?: boolean;
5101
5216
  }
5102
5217
  /**
5103
5218
  * SC6: one scene-core track as the contract writes it — `{ prop, keys: [[progress, value]], from? }`. Typed here, not
@@ -5149,6 +5264,8 @@ interface SitePattern {
5149
5264
  /** SC6: a Scene section — held while the page scrolls, moved by the scroll. Only at How animated Expressive, at
5150
5265
  * most one per page and on at most two pages (`validateSitePlan`); it never takes the look's entrances. */
5151
5266
  readonly scene?: boolean;
5267
+ /** SC6b: a Scene that plays the owner's film. Placed by `placeFilm`, never chosen: no manifest or wire lists it. */
5268
+ readonly film?: boolean;
5152
5269
  readonly node: PatternNode;
5153
5270
  }
5154
5271
  declare const SITE_PATTERNS: Readonly<Record<string, SitePattern>>;
@@ -5513,7 +5630,10 @@ declare enum SitePlanIssueCode {
5513
5630
  ReservedPageKey = "reserved-page-key",
5514
5631
  /** SC6: a Scene below How animated Expressive, a second Scene on a page, or Scenes on more than the home page and
5515
5632
  * one other — the pages answer by choosing another pattern. */
5516
- SceneBudget = "scene-budget"
5633
+ SceneBudget = "scene-budget",
5634
+ /** SC6b: the owner's film — pictures that cannot play, too few to play smoothly, or a film's pattern on a plan with
5635
+ * no film (or twice). Set by the build, never by a model. */
5636
+ Film = "film"
5517
5637
  }
5518
5638
  type SitePlanIssue = PathIssue<SitePlanIssueCode>;
5519
5639
  interface SitePlanValidation {
@@ -5920,7 +6040,11 @@ interface ShowcaseInput {
5920
6040
  business: SiteBusiness;
5921
6041
  design: DesignChoices;
5922
6042
  locale?: `${SiteLocale}`;
6043
+ /** SC6b: the film the film's Scene plays — a render-parity run serves its own; absent, `SHOWCASE_FILM`. */
6044
+ film?: FrameSequence;
5923
6045
  }
6046
+ /** SC6b: a film's description for the showcase — as many pictures as the build prepares; nothing is fetched to compose it. */
6047
+ declare const SHOWCASE_FILM: FrameSequence;
5924
6048
  /** The showcase's pages: Home (the heroes), one page per other family in library order, and the item page. */
5925
6049
  declare function showcasePages(): PagePlan[];
5926
6050
  /** Every header and footer pattern, in contract order. */
@@ -5933,7 +6057,7 @@ declare function showcaseScenes(): string[];
5933
6057
  /**
5934
6058
  * The showcase as Site Plans — as many as it takes for every header and every footer to be composed once, then one per
5935
6059
  * Scene pattern (SC6): its home page holds that Scene and nothing else, at How animated Expressive, the only level a
5936
- * Scene is composed at.
6060
+ * Scene is composed at; the film's Scene with a film (SC6b).
5937
6061
  */
5938
6062
  declare function showcasePlans(input: ShowcaseInput): SitePlan[];
5939
6063
 
@@ -6226,7 +6350,7 @@ declare function planPartOfPath(path: string): PlanPart;
6226
6350
  declare function planWireGaps(): string[];
6227
6351
  declare class PlanWireShapeError extends Error {
6228
6352
  }
6229
- /** The patterns a page section may use (every non-global one), in library order. */
6353
+ /** The patterns a page section may use (every non-global one but a film's, which `placeFilm` places), in library order. */
6230
6354
  declare function sectionPatternIds(): string[];
6231
6355
  declare function wireFieldJsonSchema(): JsonSchema;
6232
6356
  /** What an object of the wire travels without when its children are written as their own entries (a plan edit's). */
@@ -6380,7 +6504,10 @@ declare function modelCanDo(model: Pick<AiModelEntry, 'capabilities'>, step: AiS
6380
6504
  declare enum AiRefusalCode {
6381
6505
  NotInPlan = "ai.notInPlan",
6382
6506
  OutOfCredits = "ai.outOfCredits",
6507
+ /** A step used more than twice its estimate (a loop), or the build its plan's `aiCreditsPerGeneration`. */
6383
6508
  CapReached = "ai.capReached",
6509
+ /** The build asked for `GENERATION_LIMITS.plans` plans already — build one, change it with a sentence, or start over. */
6510
+ PlansUsed = "ai.plansUsed",
6384
6511
  NotConnected = "ai.notConnected",
6385
6512
  KeyInvalid = "ai.keyInvalid",
6386
6513
  PlanUnknown = "ai.planUnknown"
@@ -6394,7 +6521,9 @@ declare enum AiNextKind {
6394
6521
  /** Open Settings ▸ AI. */
6395
6522
  Settings = "settings",
6396
6523
  /** Try again later, or with less. */
6397
- Retry = "retry"
6524
+ Retry = "retry",
6525
+ /** Keep the plan there is: build it, or change it with a sentence. */
6526
+ KeepPlan = "keepPlan"
6398
6527
  }
6399
6528
  declare const AI_REFUSAL_NEXT: Readonly<Record<AiRefusalCode, AiNextKind>>;
6400
6529
  interface AiRefusal {
@@ -6404,6 +6533,8 @@ interface AiRefusal {
6404
6533
  remaining?: number;
6405
6534
  /** What the refused call would have cost, about. */
6406
6535
  estimate?: number;
6536
+ /** `PlansUsed`: how many plans one build may ask for. */
6537
+ plans?: number;
6407
6538
  next: {
6408
6539
  kind: AiNextKind;
6409
6540
  href?: string;
@@ -7177,5 +7308,54 @@ declare function stockCreditNode(index: number, credit: StockLink): SerializedNo
7177
7308
  /** Append the credit lines to the footer root (once each). Returns how many it added. */
7178
7309
  declare function addStockCredits(footer: SerializedNode, credits: ReadonlyArray<StockLink>): number;
7179
7310
 
7180
- export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_PART_TOKENS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentFieldControl, ContentFieldType, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FIELD_TYPE_OF_CONTROL, FINAL_GENERATION_STAGES, FINDING_SEVERITY, GRAMMAR_TOO_LARGE_MESSAGE, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PLAN_EDITS_ANSWER_SHAPES, PLAN_EDITS_PART_SCHEMA_NAMES, PLAN_EDIT_OP_PART, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_PARTS, PLAN_PART_KEYS, PLAN_WIRE_DERIVED, PLAN_WIRE_PART_SCHEMA_NAMES, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanPart, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SCENE_LEVEL, SCENE_OTHER_PAGES, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_FAMILY_INTENTS, STOCK_INTENT_ORDER, STOCK_INTENT_SUBJECTS, STOCK_ITEM_ASPECT, STOCK_MIN_KEPT, STOCK_PROVIDER_TERMS, STOCK_SUBJECT_WORDS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockIntent, StockOrientation, StockProviderId, StockReviewFlag, StockReviewVerdict, StockSlotKind, StockSubject, addBarItem, addStockCredits, aiStepParts, applyOwnPhotoGalleries, applyPlanEdits, applyStockItemPhotos, applyStockPhotos, archetypeIssues, aspectRatioOf, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, derivedDataSources, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, orientationOfRatio, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternListGap, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planOwnPhotos, planPartOfPath, planPartToWire, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, recipeListBinds, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, scoreStockPhoto, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, showcaseScenes, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockFits, stockGroupOf, stockItemSlots, stockKept, stockPhotoRatio, stockPhotoSubject, stockPhotoUsable, stockPhotoWords, stockSlots, stockWord, stockWords, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, withDerivedSources, wordPattern, writtenTexts };
7181
- export type { AddBarItemResult, AiEntitlementsView, AiMeter, AiModelEntry, AiRefusal, AiTokenCounts, AiTokenPrice, AuditPage, AuditPlaceholder, BarItemsEditResult, BuildNotice, BuildStepError, BuildStepFacts, BuildStepRecord, CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedNavHeader, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSection, ComposedSite, ContentFieldPlan, ContentRoles, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, CreatedRecord, DataSourcePlan, DynamicKeysAsList, EntranceAction, EntranceActionOptions, EntranceInteraction, EntranceMark, EntranceSpec, EntranceStamp, FactKey, JsonSchema, NavHeaderInput, NavHeaderOptions, NavPage, NavShapeOptions, NavShapeResult, NodeBaseline, OwnPhotoPlacement, PageArchetype, PageArchetypeId, PageArchetypeSlot, PageData, PageDataLine, PagePlan, PageSectionPlan, PageSeoPlan, PathIssue, PatternManifestEntry, PatternManifestOptions, PatternNode, PatternPath, PatternRoleNeed, PatternTrack, PlainObject, PlanChange, PlanEdit, PlanEditIssue, PlanEditResult, PlanEditsAnswer, PlanEntrancesInput, PlanWireContext, PlanWireParts, PlanWordContext, PlanWordIssue, PreflightRefusal, PublishRefusal, RecipeListBinds, RelativeDataSourcePlan, SectionEntrances, SectionInput, SectionOptions, SerializedBinding, SerializedNode, SerializedResponsive, ShapeEntry, ShapeIssueCodes, ShowcaseInput, SiteAudit, SiteAuditOptions, SiteAuditSnapshot, SiteBuildRun, SiteBusiness, SiteCheck, SiteFinding, SiteNeedId, SitePageRef, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, StockCredit, StockItemSlot, StockLink, StockNeed, StockPhoto, StockPhotoTraits, StockPlacement, StockPlanOptions, StockProviderTerms, StockReview, StockSlot, StockTarget, StrictSchemaComplexity, StrictSchemaRefusal, TemplatePlanInput, UndoKept, UndoPreview, UndoRecord, WireOptions, WrittenText };
7311
+ /**
7312
+ * The owner's film on a built site (premium sites SC6b). The owner gives a video in Analysis; the wizard's browser makes
7313
+ * it pictures (cms-client `FramesPrepareService`); the build places it. A model never picks the film's pattern — the
7314
+ * manifest and the plan wire leave it out — so ONE pure placement decides where it goes, run by the wizard to draw the
7315
+ * Plan and by the server to build: they cannot disagree.
7316
+ */
7317
+ /** The pattern a film plays in: the one whose Video is marked `film` in the contract. */
7318
+ declare const FILM_PATTERN: string;
7319
+ /** Why a film stays off the site. Each is said to the owner on the Plan step. */
7320
+ declare enum FilmUnplaced {
7321
+ /** How animated is below Expressive: no section holds the page there. */
7322
+ NotExpressive = "notExpressive",
7323
+ /** The home page does not open with a hero to play it in. */
7324
+ NoHero = "noHero",
7325
+ /** The home page already holds the page elsewhere (one Scene per page). */
7326
+ AnotherScene = "anotherScene",
7327
+ /** Too few pictures to play smoothly over the shortest hold (media-core `FILM_MIN_PICTURES`). */
7328
+ TooShort = "tooShort"
7329
+ }
7330
+ /** Where the film plays: the home page's first section when it is a hero. */
7331
+ interface FilmTarget {
7332
+ readonly page: number;
7333
+ readonly section: number;
7334
+ }
7335
+ type FilmPlacement = {
7336
+ readonly target: FilmTarget;
7337
+ readonly unplaced?: undefined;
7338
+ } | {
7339
+ readonly target?: undefined;
7340
+ readonly unplaced: FilmUnplaced;
7341
+ };
7342
+ /** Where a film WOULD play on this plan (the Plan step asks before the pictures exist). */
7343
+ declare function filmPlacement(plan: Pick<SitePlan, 'design' | 'pages'>): FilmPlacement;
7344
+ interface PlacedFilm {
7345
+ readonly plan: SitePlan;
7346
+ readonly unplaced?: FilmUnplaced;
7347
+ }
7348
+ /**
7349
+ * The plan with the owner's film in it: `film` set, and the home page's hero becomes the film's pattern, keeping the
7350
+ * hero's words and links that the film's pattern has. A film that cannot play (unreadable, too few pictures) or a plan
7351
+ * with no place for it leaves the plan as it was — the hero keeps its picture, and the builder can add the film later.
7352
+ */
7353
+ declare function placeFilm(plan: SitePlan, sequence: unknown): PlacedFilm;
7354
+ /** The film's pictures as the composer stamps them, and the height its Scene holds the page for. */
7355
+ declare function filmOfPlan(plan: Pick<SitePlan, 'film'>): {
7356
+ sequence: FrameSequence;
7357
+ heightVh: number;
7358
+ } | null;
7359
+
7360
+ export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_PART_TOKENS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentFieldControl, ContentFieldType, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FIELD_TYPE_OF_CONTROL, FILM_PATTERN, FINAL_GENERATION_STAGES, FINDING_SEVERITY, FilmUnplaced, GRAMMAR_TOO_LARGE_MESSAGE, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PLAN_EDITS_ANSWER_SHAPES, PLAN_EDITS_PART_SCHEMA_NAMES, PLAN_EDIT_OP_PART, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_PARTS, PLAN_PART_KEYS, PLAN_WIRE_DERIVED, PLAN_WIRE_PART_SCHEMA_NAMES, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanPart, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SCENE_LEVEL, SCENE_OTHER_PAGES, SHOWCASE_FILM, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_FAMILY_INTENTS, STOCK_INTENT_ORDER, STOCK_INTENT_SUBJECTS, STOCK_ITEM_ASPECT, STOCK_MIN_KEPT, STOCK_PROVIDER_TERMS, STOCK_SUBJECT_WORDS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockIntent, StockOrientation, StockProviderId, StockReviewFlag, StockReviewVerdict, StockSlotKind, StockSubject, addBarItem, addStockCredits, aiStepParts, applyOwnPhotoGalleries, applyPlanEdits, applyStockItemPhotos, applyStockPhotos, archetypeIssues, aspectRatioOf, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, derivedDataSources, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, filmOfPlan, filmPlacement, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, orientationOfRatio, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternListGap, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, placeFilm, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planOwnPhotos, planPartOfPath, planPartToWire, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, recipeListBinds, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, scoreStockPhoto, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, showcaseScenes, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockFits, stockGroupOf, stockItemSlots, stockKept, stockPhotoRatio, stockPhotoSubject, stockPhotoUsable, stockPhotoWords, stockSlots, stockWord, stockWords, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, withDerivedSources, wordPattern, writtenTexts };
7361
+ export type { AddBarItemResult, AiEntitlementsView, AiMeter, AiModelEntry, AiRefusal, AiTokenCounts, AiTokenPrice, AuditPage, AuditPlaceholder, BarItemsEditResult, BuildNotice, BuildStepError, BuildStepFacts, BuildStepRecord, CmsDataSourcePlan, ComposeContext, ComposeIssue, ComposedDataSource, ComposedItem, ComposedNavHeader, ComposedPage, ComposedPlaceholder, ComposedRoute, ComposedSection, ComposedSite, ContentFieldPlan, ContentRoles, ContentSeedPlan, ContentTypePlan, CreateNodeOptions, CreatedRecord, DataSourcePlan, DynamicKeysAsList, EntranceAction, EntranceActionOptions, EntranceInteraction, EntranceMark, EntranceSpec, EntranceStamp, FactKey, FilmPlacement, FilmTarget, JsonSchema, NavHeaderInput, NavHeaderOptions, NavPage, NavShapeOptions, NavShapeResult, NodeBaseline, OwnPhotoPlacement, PageArchetype, PageArchetypeId, PageArchetypeSlot, PageData, PageDataLine, PagePlan, PageSectionPlan, PageSeoPlan, PathIssue, PatternManifestEntry, PatternManifestOptions, PatternNode, PatternPath, PatternRoleNeed, PatternTrack, PlacedFilm, PlainObject, PlanChange, PlanEdit, PlanEditIssue, PlanEditResult, PlanEditsAnswer, PlanEntrancesInput, PlanFilm, PlanWireContext, PlanWireParts, PlanWordContext, PlanWordIssue, PreflightRefusal, PublishRefusal, RecipeListBinds, RelativeDataSourcePlan, SectionEntrances, SectionInput, SectionOptions, SerializedBinding, SerializedNode, SerializedResponsive, ShapeEntry, ShapeIssueCodes, ShowcaseInput, SiteAudit, SiteAuditOptions, SiteAuditSnapshot, SiteBuildRun, SiteBusiness, SiteCheck, SiteFinding, SiteNeedId, SitePageRef, SitePattern, SitePlan, SitePlanIssue, SitePlanSource, SitePlanValidation, SiteTemplate, StockCredit, StockItemSlot, StockLink, StockNeed, StockPhoto, StockPhotoTraits, StockPlacement, StockPlanOptions, StockProviderTerms, StockReview, StockSlot, StockTarget, StrictSchemaComplexity, StrictSchemaRefusal, TemplatePlanInput, UndoKept, UndoPreview, UndoRecord, WireOptions, WrittenText };
@@ -1,6 +1,7 @@
1
1
  import { MotionId, MoodId, TintId, DensityId, DesignMotion, DesignChoices, DesignMode, StylePreset, ShapeId, AnimateId } from '@vectoriox/iox-ui/design-core';
2
2
  import { FactKey, PathIssue, PlainObject, JsonSchema, SiteTemplate, ShapeEntry, AiGenerationStage, AiStep, SitePlan, PlanEdit, PlanChange, SiteAudit } from '@vectoriox/iox-ui/generation-core';
3
3
  export { JsonSchema } from '@vectoriox/iox-ui/generation-core';
4
+ import { FrameSequence } from '@vectoriox/iox-ui/media-core';
4
5
 
5
6
  declare const SPEC_HARNESS_SOURCE: {
6
7
  readonly repo: "nextlevelbuilder/ui-ux-pro-max-skill";
@@ -2948,11 +2949,13 @@ interface AiGenerationView {
2948
2949
  stage: `${AiGenerationStage}`;
2949
2950
  /** The model step the runner is working on; null = the owner's move. */
2950
2951
  task: `${AiStep}` | null;
2952
+ /** `film`: the owner's film as pictures (SC6b), told by the wizard at Build — `placeFilm` places it. */
2951
2953
  intake: {
2952
2954
  prompt: string;
2953
2955
  files: GenerationFileView[];
2954
2956
  addresses: string[];
2955
2957
  auto: boolean;
2958
+ film?: FrameSequence;
2956
2959
  };
2957
2960
  readers: GenerationReadersView;
2958
2961
  reads: {
@@ -2984,7 +2987,9 @@ interface AiGenerationView {
2984
2987
  siteBuildId?: string;
2985
2988
  models: Partial<Record<`${AiStep}`, string>>;
2986
2989
  estimate: number;
2987
- cap: number;
2990
+ /** The plan's `aiCreditsPerGeneration` for the whole build, or null when it sets none (a step's own ceiling is
2991
+ * twice that step's estimate — going back and planning again starts a new step). */
2992
+ cap: number | null;
2988
2993
  /** Derived from the ledger — the running credit total. */
2989
2994
  spent: number;
2990
2995
  error?: {
@@ -3006,12 +3011,14 @@ interface GenerationStartRequest {
3006
3011
  }>;
3007
3012
  addresses?: string[];
3008
3013
  }
3009
- /** The longest message, the most files and addresses a generation starts from (Start's limits). */
3014
+ /** The longest message, the most files and addresses a generation starts from (Start's limits), and how many plans
3015
+ * one build may ask for (a Design pick, a Try again or "Build with these" each asks one). */
3010
3016
  declare const GENERATION_LIMITS: {
3011
3017
  readonly promptChars: 4000;
3012
3018
  readonly files: 12;
3013
3019
  readonly addresses: 3;
3014
3020
  readonly sentenceChars: 500;
3021
+ readonly plans: 5;
3015
3022
  };
3016
3023
 
3017
3024
  /**
@@ -111,6 +111,24 @@ declare const PHONE_LONG_SIDE = 1100;
111
111
  declare function fittedFrameSize(width: number, height: number, longSide: number): FrameSizes;
112
112
  /** How many pictures a video of `seconds` becomes. */
113
113
  declare function videoFrameCount(seconds: number): number;
114
+ /**
115
+ * Pictures per screen of scrolling at which a played film stays smooth as a glide slows to a stop. Measured on Doron's
116
+ * iPhone (2026-11-05): Studio Alma's film at about 11 a screen stepped, at about 55 it did not.
117
+ */
118
+ declare const FILM_SMOOTH_PER_SCREEN = 40;
119
+ /** The fewest and most screens a film the build places holds the page for (SC6b): long enough to feel, short enough to pass. */
120
+ declare const FILM_MIN_SCREENS = 1;
121
+ declare const FILM_MAX_SCREENS = 4;
122
+ /** What the build prepares a film to: about 60 pictures a screen at the longest hold — denser than smooth, never wasted. */
123
+ declare const FILM_PREPARED_PICTURES: number;
124
+ /** The fewest pictures a placed film may have: smooth over the shortest hold. */
125
+ declare const FILM_MIN_PICTURES: number;
126
+ /**
127
+ * The screens a film of `count` pictures holds the page for (SC6b, the composer's density rule): the stretches between
128
+ * pictures are what scroll, so `(count − 1) ÷ 40`, rounded DOWN to a tenth (150 pictures over 3.75 screens measure 39.7 a
129
+ * screen, thin), within the hold's limits. Null: too few pictures to play smoothly over the shortest hold.
130
+ */
131
+ declare function filmScreensFor(count: number): number | null;
114
132
  /** Which of `sourceCount` pictures make a set of `count`, spread evenly, first and last always in. */
115
133
  declare function sampledFrameIndexes(sourceCount: number, count: number): number[];
116
134
  /** Rendered frames in their real order: `frame2` before `frame10`. */
@@ -473,5 +491,5 @@ declare function attachVideos(container: ParentNode, options?: VideoRuntimeOptio
473
491
  /** The runtime already running at `el`, if any. */
474
492
  declare function videoRuntimeOf(el: Element | null | undefined): VideoRuntime | null;
475
493
 
476
- export { CONSENT_GRANT_EVENT, CONSENT_PREFERENCES_EVENT, FRAMES_PHONE_MAX_WIDTH, FRAMES_SHOWING_ATTRIBUTE, FRAMES_UNPACKED_BYTES, FRAME_BLEND_STEPS, FRAME_SEQUENCE_ID, FrameExtension, FramePlayer, FrameSet, FramesClass, MAX_FRAMES, MAX_FRAME_BYTES, PHONE_LONG_SIDE, VIDEO_EMBED_ORIGINS, VIDEO_NODE_TYPE, VIDEO_PROVIDER_COMPANY, VIDEO_PROVIDER_LABEL, VIDEO_WORDS, VideoAttribute, VideoClass, VideoIcon, VideoKind, VideoMode, VideoRuntime, VideoSource, VideoTrait, WIDE_LONG_SIDE, attachVideoAt, attachVideos, documentConsentTransport, fittedFrameSize, frameBlendAt, frameFileName, frameIndexAt, frameLoadOrder, framePosition, frameSetFor, frameSummary, frameUrl, framesToHold, isThirdPartyPoster, nearestLoadedFrame, normalizeFrameSequence, parseVideoUrl, phoneFrameSrcset, planVideo, readConsentRecord, sampledFrameIndexes, serializeVideoAttributes, sortFrameFiles, unpackedFrameCount, videoConsentCategory, videoEmbedUrl, videoFrameCount, videoInnerHtml, videoPlayLabel, videoPosterFileName, videoProp, videoRefOf, videoRootAttributes, videoRuntimeOf, videoSourceOf, videoThumbnailRequest, videoWords };
494
+ export { CONSENT_GRANT_EVENT, CONSENT_PREFERENCES_EVENT, FILM_MAX_SCREENS, FILM_MIN_PICTURES, FILM_MIN_SCREENS, FILM_PREPARED_PICTURES, FILM_SMOOTH_PER_SCREEN, FRAMES_PHONE_MAX_WIDTH, FRAMES_SHOWING_ATTRIBUTE, FRAMES_UNPACKED_BYTES, FRAME_BLEND_STEPS, FRAME_SEQUENCE_ID, FrameExtension, FramePlayer, FrameSet, FramesClass, MAX_FRAMES, MAX_FRAME_BYTES, PHONE_LONG_SIDE, VIDEO_EMBED_ORIGINS, VIDEO_NODE_TYPE, VIDEO_PROVIDER_COMPANY, VIDEO_PROVIDER_LABEL, VIDEO_WORDS, VideoAttribute, VideoClass, VideoIcon, VideoKind, VideoMode, VideoRuntime, VideoSource, VideoTrait, WIDE_LONG_SIDE, attachVideoAt, attachVideos, documentConsentTransport, filmScreensFor, fittedFrameSize, frameBlendAt, frameFileName, frameIndexAt, frameLoadOrder, framePosition, frameSetFor, frameSummary, frameUrl, framesToHold, isThirdPartyPoster, nearestLoadedFrame, normalizeFrameSequence, parseVideoUrl, phoneFrameSrcset, planVideo, readConsentRecord, sampledFrameIndexes, serializeVideoAttributes, sortFrameFiles, unpackedFrameCount, videoConsentCategory, videoEmbedUrl, videoFrameCount, videoInnerHtml, videoPlayLabel, videoPosterFileName, videoProp, videoRefOf, videoRootAttributes, videoRuntimeOf, videoSourceOf, videoThumbnailRequest, videoWords };
477
495
  export type { ConsentGrantDetail, ConsentTransport, FrameBlend, FramePlayerOptions, FrameSequence, FrameSetInfo, FrameSizes, VideoMarkupOptions, VideoNodeLike, VideoPlan, VideoRef, VideoRuntimeOptions, VideoThumbnailRequest, VideoWords };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.78.0",
3
+ "version": "5.80.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -218,10 +218,7 @@ declare const SCENE_MAX_SCREENS = 20;
218
218
  */
219
219
  declare function sceneScreensOf(height: string | null | undefined): number | null;
220
220
  declare function sceneHeightForScreens(screens: number): string;
221
- /**
222
- * Pictures per screen of scrolling at which a played film stays smooth as a glide slows to a stop. Measured on Doron's
223
- * iPhone (2026-11-05): Studio Alma's film at about 11 a screen stepped, at about 55 it did not.
224
- */
221
+ /** Pictures per screen of scrolling at which a played film stays smooth (media-core's, which the composer sizes by). */
225
222
  declare const SCENE_FILM_SMOOTH = 40;
226
223
  interface SceneFilmDensity {
227
224
  /** Pictures per screen of scrolling over most of the play: half its scroll holds at least this many. */
@@ -177,7 +177,10 @@ export function modelCanDo(model: Pick<AiModelEntry, 'capabilities'>, step: AiSt
177
177
  export enum AiRefusalCode {
178
178
  NotInPlan = 'ai.notInPlan',
179
179
  OutOfCredits = 'ai.outOfCredits',
180
+ /** A step used more than twice its estimate (a loop), or the build its plan's `aiCreditsPerGeneration`. */
180
181
  CapReached = 'ai.capReached',
182
+ /** The build asked for `GENERATION_LIMITS.plans` plans already — build one, change it with a sentence, or start over. */
183
+ PlansUsed = 'ai.plansUsed',
181
184
  NotConnected = 'ai.notConnected',
182
185
  KeyInvalid = 'ai.keyInvalid',
183
186
  PlanUnknown = 'ai.planUnknown',
@@ -188,6 +191,7 @@ export const AI_REFUSAL_STATUS: Readonly<Record<AiRefusalCode, number>> = {
188
191
  [AiRefusalCode.NotInPlan]: 403,
189
192
  [AiRefusalCode.OutOfCredits]: 402,
190
193
  [AiRefusalCode.CapReached]: 402,
194
+ [AiRefusalCode.PlansUsed]: 409,
191
195
  [AiRefusalCode.NotConnected]: 409,
192
196
  [AiRefusalCode.KeyInvalid]: 409,
193
197
  [AiRefusalCode.PlanUnknown]: 503,
@@ -201,12 +205,15 @@ export enum AiNextKind {
201
205
  Settings = 'settings',
202
206
  /** Try again later, or with less. */
203
207
  Retry = 'retry',
208
+ /** Keep the plan there is: build it, or change it with a sentence. */
209
+ KeepPlan = 'keepPlan',
204
210
  }
205
211
 
206
212
  export const AI_REFUSAL_NEXT: Readonly<Record<AiRefusalCode, AiNextKind>> = {
207
213
  [AiRefusalCode.NotInPlan]: AiNextKind.Contact,
208
214
  [AiRefusalCode.OutOfCredits]: AiNextKind.Contact,
209
215
  [AiRefusalCode.CapReached]: AiNextKind.Retry,
216
+ [AiRefusalCode.PlansUsed]: AiNextKind.KeepPlan,
210
217
  [AiRefusalCode.NotConnected]: AiNextKind.Settings,
211
218
  [AiRefusalCode.KeyInvalid]: AiNextKind.Settings,
212
219
  [AiRefusalCode.PlanUnknown]: AiNextKind.Retry,
@@ -219,6 +226,8 @@ export interface AiRefusal {
219
226
  remaining?: number;
220
227
  /** What the refused call would have cost, about. */
221
228
  estimate?: number;
229
+ /** `PlansUsed`: how many plans one build may ask for. */
230
+ plans?: number;
222
231
  next: { kind: AiNextKind; href?: string };
223
232
  }
224
233
 
@@ -10,6 +10,7 @@ import {
10
10
  } from '@vectoriox/iox-ui/composite-core';
11
11
 
12
12
  import { EntranceMark, EntrancePart, planEntrances } from './entrances';
13
+ import { filmOfPlan } from './film';
13
14
  import { NODE_BASELINES } from './node-baselines.generated';
14
15
  import {
15
16
  BindScope, CardBindTarget, parsePatternPath, PatternDirective, PatternNode, PatternOpens, PatternPlaceholder, PatternRecipe, PatternTrack, SitePattern, patternById,
@@ -206,6 +207,8 @@ const IMAGE = 'Image';
206
207
  const VIDEO = 'Video';
207
208
  /** A Video's source prop (media-core `VideoTrait.Source` — not `source`, which names a data source). */
208
209
  const VIDEO_SOURCE_PROP = 'videoSource';
210
+ /** A Video's pictures the scroll plays (media-core `VideoTrait.Frames`). */
211
+ const VIDEO_FRAMES_PROP = 'frames';
209
212
  const DIALOG = 'Dialog';
210
213
  const SUBMIT_BUTTON = 'SubmitButton';
211
214
  const FORM_SUCCESS = 'FormSuccess';
@@ -602,6 +605,13 @@ function composeNode(given: PatternNode, id: string, repeater: RepeaterScope | n
602
605
  setProp(node, VIDEO_SOURCE_PROP, 'file');
603
606
  setProp(node, 'mode', pn.video);
604
607
  }
608
+ // SC6b: a film pattern's Video plays the owner's pictures (the build places the pattern only with a film).
609
+ if (pn.film) {
610
+ if (type !== VIDEO) throw new ComposeError(`${id}: "film" belongs on a Video`);
611
+ const film = filmOfPlan(ctx.plan);
612
+ if (!film) throw new ComposeError(`${id}: a film's pattern on a plan with no film`);
613
+ setProp(node, VIDEO_FRAMES_PROP, film.sequence);
614
+ }
605
615
  if (pn.ph === PatternPlaceholder.Video) {
606
616
  setProp(node, 'src', '');
607
617
  ctx.placeholders.push({ nodeId: id, owner: ctx.owner, kind: PlaceholderKind.Video, text: '' });
@@ -1013,6 +1023,9 @@ function composePattern(pattern: SitePattern, id: string, ctx: Ctx, motion: Sect
1013
1023
  wireShownWhen(node, own.shownWhen ?? []);
1014
1024
  // A Scene moves by the scroll alone: the look's entrances would fade its pinned stage in on arrival (SC6).
1015
1025
  if (motion && !pattern.global && !pattern.scene) stampEntrances(node, pattern.family, own.entrances ?? [], motion);
1026
+ // SC6b, the density rule: a film's Scene holds the page as long as its pictures stay smooth, and no longer.
1027
+ const film = pattern.film ? filmOfPlan(ctx.plan) : null;
1028
+ if (film) setStyleProps(node, { height: `${film.heightVh}vh` });
1016
1029
  node.label = pattern.label;
1017
1030
  return node;
1018
1031
  }
@@ -0,0 +1,91 @@
1
+ import { filmScreensFor, FrameSequence, normalizeFrameSequence } from '@vectoriox/iox-ui/media-core';
2
+
3
+ import { patternById, patternLinkSlots, patternSlots, SCENE_LEVEL, SITE_PATTERNS } from './patterns';
4
+ import { PageSectionPlan, SitePlan } from './site-plan.model';
5
+
6
+ /**
7
+ * The owner's film on a built site (premium sites SC6b). The owner gives a video in Analysis; the wizard's browser makes
8
+ * it pictures (cms-client `FramesPrepareService`); the build places it. A model never picks the film's pattern — the
9
+ * manifest and the plan wire leave it out — so ONE pure placement decides where it goes, run by the wizard to draw the
10
+ * Plan and by the server to build: they cannot disagree.
11
+ */
12
+
13
+ /** The pattern a film plays in: the one whose Video is marked `film` in the contract. */
14
+ export const FILM_PATTERN: string = Object.entries(SITE_PATTERNS).find(([, p]) => p.film)?.[0] ?? '';
15
+
16
+ /** Why a film stays off the site. Each is said to the owner on the Plan step. */
17
+ export enum FilmUnplaced {
18
+ /** How animated is below Expressive: no section holds the page there. */
19
+ NotExpressive = 'notExpressive',
20
+ /** The home page does not open with a hero to play it in. */
21
+ NoHero = 'noHero',
22
+ /** The home page already holds the page elsewhere (one Scene per page). */
23
+ AnotherScene = 'anotherScene',
24
+ /** Too few pictures to play smoothly over the shortest hold (media-core `FILM_MIN_PICTURES`). */
25
+ TooShort = 'tooShort',
26
+ }
27
+
28
+ /** Where the film plays: the home page's first section when it is a hero. */
29
+ export interface FilmTarget {
30
+ readonly page: number;
31
+ readonly section: number;
32
+ }
33
+
34
+ export type FilmPlacement = { readonly target: FilmTarget; readonly unplaced?: undefined } | { readonly target?: undefined; readonly unplaced: FilmUnplaced };
35
+
36
+ /** Where a film WOULD play on this plan (the Plan step asks before the pictures exist). */
37
+ export function filmPlacement(plan: Pick<SitePlan, 'design' | 'pages'>): FilmPlacement {
38
+ if ((plan.design as { animate?: string }).animate !== SCENE_LEVEL) return { unplaced: FilmUnplaced.NotExpressive };
39
+ const page = plan.pages.findIndex(p => p.isHome);
40
+ const sections = page >= 0 ? plan.pages[page].sections : [];
41
+ const first = patternById(sections[0]?.pattern);
42
+ if (!first || first.family !== FILM_FAMILY) return { unplaced: FilmUnplaced.NoHero };
43
+ if (sections.slice(1).some(s => patternById(s.pattern)?.scene)) return { unplaced: FilmUnplaced.AnotherScene };
44
+ return { target: { page, section: 0 } };
45
+ }
46
+
47
+ const FILM_FAMILY = SITE_PATTERNS[FILM_PATTERN]?.family ?? 'hero';
48
+
49
+ export interface PlacedFilm {
50
+ readonly plan: SitePlan;
51
+ readonly unplaced?: FilmUnplaced;
52
+ }
53
+
54
+ /**
55
+ * The plan with the owner's film in it: `film` set, and the home page's hero becomes the film's pattern, keeping the
56
+ * hero's words and links that the film's pattern has. A film that cannot play (unreadable, too few pictures) or a plan
57
+ * with no place for it leaves the plan as it was — the hero keeps its picture, and the builder can add the film later.
58
+ */
59
+ export function placeFilm(plan: SitePlan, sequence: unknown): PlacedFilm {
60
+ const film = normalizeFrameSequence(sequence);
61
+ if (!film || filmScreensFor(film.wide.count) === null) return { plan, unplaced: FilmUnplaced.TooShort };
62
+ const placement = filmPlacement(plan);
63
+ if (!placement.target) return { plan, unplaced: placement.unplaced };
64
+ const { page, section } = placement.target;
65
+ const pages = plan.pages.map((p, i) => i !== page ? p : {
66
+ ...p,
67
+ sections: p.sections.map((s, j) => (j === section ? filmSection(s) : s)),
68
+ });
69
+ return { plan: { ...plan, pages, film: { sequence: film } } };
70
+ }
71
+
72
+ function filmSection(hero: PageSectionPlan): PageSectionPlan {
73
+ const pattern = SITE_PATTERNS[FILM_PATTERN];
74
+ const slots = new Set(patternSlots(pattern));
75
+ const links = new Set(patternLinkSlots(pattern));
76
+ const keep = (values: Readonly<Record<string, string>> | undefined, allowed: Set<string>) => {
77
+ const kept = Object.entries(values ?? {}).filter(([key]) => allowed.has(key));
78
+ return kept.length ? Object.fromEntries(kept) : undefined;
79
+ };
80
+ const keptSlots = keep(hero.slots, slots);
81
+ const keptLinks = keep(hero.links, links);
82
+ return { pattern: FILM_PATTERN, ...(keptSlots ? { slots: keptSlots } : {}), ...(keptLinks ? { links: keptLinks } : {}) };
83
+ }
84
+
85
+ /** The film's pictures as the composer stamps them, and the height its Scene holds the page for. */
86
+ export function filmOfPlan(plan: Pick<SitePlan, 'film'>): { sequence: FrameSequence; heightVh: number } | null {
87
+ const sequence = normalizeFrameSequence(plan.film?.sequence);
88
+ const screens = sequence ? filmScreensFor(sequence.wide.count) : null;
89
+ // A Scene's root holds the page for its height less the one screen its stage fills.
90
+ return sequence && screens !== null ? { sequence, heightVh: Math.round((screens + 1) * 100) } : null;
91
+ }
@@ -93,6 +93,8 @@ export interface PatternNode {
93
93
  /** SC6: a Scene layer's scene-core tracks, written to the node's `sceneTracks` (and `scenePhoneTracks`). */
94
94
  readonly tracks?: ReadonlyArray<PatternTrack>;
95
95
  readonly phoneTracks?: ReadonlyArray<PatternTrack>;
96
+ /** SC6b: the Video a film pattern plays the owner's film in (its `frames`). */
97
+ readonly film?: boolean;
96
98
  }
97
99
 
98
100
  /**
@@ -154,6 +156,8 @@ export interface SitePattern {
154
156
  /** SC6: a Scene section — held while the page scrolls, moved by the scroll. Only at How animated Expressive, at
155
157
  * most one per page and on at most two pages (`validateSitePlan`); it never takes the look's entrances. */
156
158
  readonly scene?: boolean;
159
+ /** SC6b: a Scene that plays the owner's film. Placed by `placeFilm`, never chosen: no manifest or wire lists it. */
160
+ readonly film?: boolean;
157
161
  readonly node: PatternNode;
158
162
  }
159
163
 
@@ -352,7 +356,7 @@ export interface PatternManifestOptions {
352
356
  */
353
357
  export function patternManifest(density?: string | null, options: PatternManifestOptions = {}): PatternManifestEntry[] {
354
358
  const scenes = !('animate' in options) || options.animate === SCENE_LEVEL;
355
- return Object.entries(SITE_PATTERNS).filter(([, p]) => scenes || !p.scene).map(([id, p]) => ({
359
+ return Object.entries(SITE_PATTERNS).filter(([, p]) => !p.film && (scenes || !p.scene)).map(([id, p]) => ({
356
360
  id, family: p.family, label: p.label, intent: p.intent, ...(p.global ? { global: p.global } : {}),
357
361
  slots: Object.fromEntries(Object.keys(patternSlotBudgets(p)).map(slot => [slot, slotBudget(p, slot, density) as number])),
358
362
  links: patternLinkSlots(p),
@@ -196,6 +196,8 @@ export const PLAN_WIRE_DERIVED: Readonly<Record<string, string>> = {
196
196
  sourceTemplate: 'the AI never writes a template plan',
197
197
  sourceAi: 'this run (the context)',
198
198
  design: "the owner's pick (the context)",
199
+ 'plan.film': "the owner's film, set by the build (SC6b)",
200
+ film: "the owner's film, set by the build (SC6b)",
199
201
  };
200
202
 
201
203
  const wireText = (spec: WireKey): string => {
@@ -252,9 +254,9 @@ const orNone = (values: ReadonlyArray<string>): JsonSchema => jsonOneOf([NONE, .
252
254
  const closedOrText = (values: ReadonlyArray<string>): JsonSchema => (values.length ? jsonOneOf(values) : jsonText());
253
255
  const shaped = (entry: `${PlanWireEntry}`, properties: Record<string, JsonSchema>) => jsonShapedObject(PLAN_WIRE_SHAPE[entry], properties, `wire ${entry}`);
254
256
 
255
- /** The patterns a page section may use (every non-global one), in library order. */
257
+ /** The patterns a page section may use (every non-global one but a film's, which `placeFilm` places), in library order. */
256
258
  export function sectionPatternIds(): string[] {
257
- return Object.entries(SITE_PATTERNS).filter(([, p]) => !p.global).map(([id]) => id);
259
+ return Object.entries(SITE_PATTERNS).filter(([, p]) => !p.global && !p.film).map(([id]) => id);
258
260
  }
259
261
 
260
262
  const sectionPatterns = () => sectionPatternIds().map(id => SITE_PATTERNS[id]);