@bettercms-ai/types 1.37.0 → 1.39.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.
package/dist/index.d.ts CHANGED
@@ -48,7 +48,7 @@ interface ContentModel {
48
48
  * by the API but normalized to `array` + zones on write, so stored/delivered
49
49
  * content never contains them.
50
50
  */
51
- type ContentModelFieldType = "text" | "richtext" | "document" | "image" | "boolean" | "number" | "select" | "reference" | "multi-reference" | "multiReference" | "array" | "date" | "datetime" | "longtext" | "slug" | "email" | "phone" | "link" | "color" | "json" | "file" | "component-ref" | "modular" | "sections" | "location" | "button";
51
+ type ContentModelFieldType = "text" | "richtext" | "document" | "image" | "boolean" | "number" | "select" | "reference" | "multi-reference" | "multiReference" | "array" | "date" | "datetime" | "longtext" | "slug" | "email" | "phone" | "link" | "color" | "json" | "file" | "component-ref" | "modular" | "sections" | "location" | "button" | "multiselect" | "icon";
52
52
  type ContentModelKind = "model" | "block";
53
53
  /**
54
54
  * How a fieldset renders in the dock. ABSENT ≡ today's flat list (V2): a legacy `{id, name}`
@@ -129,6 +129,11 @@ interface FieldConfig {
129
129
  max?: number;
130
130
  /** date / datetime */
131
131
  includeTime?: boolean;
132
+ /**
133
+ * date / datetime with a time: "site" (the project's own zone) or an IANA zone name. When set,
134
+ * a stored value must carry `Z` or a numeric offset, so it names one instant in every region.
135
+ */
136
+ timezone?: string;
132
137
  /** modular: SLUGS of the kind='block' models this field accepts. Non-empty. */
133
138
  blockSlugs?: string[];
134
139
  /** modular: bounds on how many block instances the field may hold. */
@@ -201,6 +206,20 @@ interface FieldConfig {
201
206
  * field past that gate.
202
207
  */
203
208
  type ContentModelFieldPlacement = "cover" | "title" | "excerpt" | "body" | "inline" | "panel";
209
+ /**
210
+ * Editor-only conditional visibility: the field (or component prop) shows only while the
211
+ * sibling keys its rules name hold these values. `isSet` reads no `value`: it passes once the
212
+ * source holds anything (`false` and `0` count; "", null and an empty list do not).
213
+ */
214
+ interface ShowIfRule {
215
+ field: string;
216
+ op: "eq" | "neq" | "in" | "isSet";
217
+ value?: string | string[];
218
+ }
219
+ interface ShowIf {
220
+ match: "all" | "any";
221
+ rules: ShowIfRule[];
222
+ }
204
223
  interface ContentModelField {
205
224
  key: string;
206
225
  label: string;
@@ -227,6 +246,18 @@ interface ContentModelField {
227
246
  * Field ORDER is the array index — there is deliberately no `order` property.
228
247
  */
229
248
  placement?: ContentModelFieldPlacement;
249
+ /** Editor-only conditional visibility; a hidden required field is not required. @see ShowIf */
250
+ showIf?: ShowIf;
251
+ /**
252
+ * A value another system owns (a store, job board or feed id).
253
+ * - `"developer"`: only a caller with schema authority (a key with `schema:manage`, a person
254
+ * with schema access) may change the value on an entry, or add or remove this lock. Anyone
255
+ * else is refused with 403 `FIELD_LOCKED`. Needs {@link lockReason}.
256
+ * - `true`: a marker the editor renders read-only. The API does not enforce it.
257
+ */
258
+ readOnly?: true | "developer";
259
+ /** Which system owns the value; shown on the refusal. At most 200 characters. */
260
+ lockReason?: string;
230
261
  /**
231
262
  * Authoring chrome — how the field PRESENTS in the editor. Never delivered, never read by a
232
263
  * renderer, and never able to move a value; the same contract as {@link placement}.
@@ -284,9 +315,10 @@ interface ContentModelFieldUi {
284
315
  /**
285
316
  * Which editor control a LEAF renders with. ABSENT ≡ the type's default (a dropdown for a
286
317
  * `select`, a plain input for a `number`). `segmented` is valid on a single-choice `select`;
287
- * `slider` on a `number` whose config.min < config.max.
318
+ * `slider` on a `number` whose config.min < config.max; `rating` on a `number` with numeric
319
+ * config.min, max and step (stars).
288
320
  */
289
- control?: "segmented" | "slider";
321
+ control?: "segmented" | "slider" | "rating";
290
322
  /** The branch row's icon. Nesting fields only. */
291
323
  icon?: DockIconName;
292
324
  /** What one item is called ("Slide", "Member"), 1–24 chars. Nesting fields only. */
@@ -386,6 +418,14 @@ type ButtonSizeName = (typeof BUTTON_SIZES)[number];
386
418
  * object. Only `label` and `href` are required; every look key is optional and absent reads as
387
419
  * today's default (Primary, medium, no arrow, no icon, same tab).
388
420
  */
421
+ declare const LINK_RELS: readonly ["nofollow", "sponsored", "ugc"];
422
+ type LinkRel = (typeof LINK_RELS)[number];
423
+ /**
424
+ * The target and rel attributes of an anchor, as one string. A new tab keeps the `noopener
425
+ * noreferrer` pair; a link relation joins it in the SAME rel attribute, so an anchor never
426
+ * carries two.
427
+ */
428
+ declare function anchorTargetRel(newTab: boolean, rel?: unknown): string;
389
429
  interface ButtonValue {
390
430
  label: string;
391
431
  href: string;
@@ -394,14 +434,24 @@ interface ButtonValue {
394
434
  arrow?: boolean;
395
435
  icon?: ButtonIcon | null;
396
436
  newTab?: boolean;
437
+ /** Sponsored, untrusted or user-generated destination. Absent ⇒ no rel beyond a new tab's own. */
438
+ rel?: LinkRel;
397
439
  }
440
+ /** A stored button value as it renders: `rel` stays optional, every other key is filled. */
441
+ type ReadButtonValue = Required<Omit<ButtonValue, "rel">> & Pick<ButtonValue, "rel">;
398
442
  /** A stored button value as it renders: every key read through the same allow-lists as a block. */
399
- declare function readButtonValue(value: unknown): Required<ButtonValue>;
443
+ declare function readButtonValue(value: unknown): ReadButtonValue;
400
444
  /** Why a value is not a button, or null when it is. Look keys are checked only when present. */
401
445
  declare function buttonValueProblem(value: unknown): string | null;
402
446
  /** The look keys an instance of a Button component may override, beside its label and link. */
403
447
  declare const BUTTON_LOOK_KEYS: readonly ["variant", "size", "arrow", "icon", "newTab"];
404
448
  type ButtonLookKey = (typeof BUTTON_LOOK_KEYS)[number];
449
+ /**
450
+ * A `button` PROP's value as the `button` BLOCK props it writes, for every lane that applies an
451
+ * instance's overrides. `label` lands on `text`, and only the keys the value names come back, so
452
+ * an override carrying a label alone keeps the definition's href and look.
453
+ */
454
+ declare function buttonBlockProps(value: unknown): Record<string, unknown>;
405
455
  interface PropDefLike {
406
456
  key: string;
407
457
  label?: string;
@@ -430,7 +480,7 @@ declare function withButtonLookProps<P extends PropDefLike>(def: ComponentLike<P
430
480
  * instance's overrides written onto it — for a site rendering the instance with `BcmsButton`.
431
481
  * Null when the component is not a Button (not category `button`, or not exactly one button).
432
482
  */
433
- declare function buttonValueOfInstance<P extends PropDefLike>(def: ComponentLike<P>, overrides: Record<string, unknown> | null | undefined): Required<ButtonValue> | null;
483
+ declare function buttonValueOfInstance<P extends PropDefLike>(def: ComponentLike<P>, overrides: Record<string, unknown> | null | undefined): ReadButtonValue | null;
434
484
 
435
485
  /**
436
486
  * ContentBlock — a discriminated union of all CMS block types.
@@ -496,6 +546,11 @@ interface BlockStyle {
496
546
  bgImage?: string;
497
547
  /** A button's look while the pointer is over it. Only the paint changes; unset keys keep the rest. */
498
548
  hover?: BlockHoverStyle;
549
+ /**
550
+ * Breakpoints the block is hidden on (`display:none` there only; the block stays in the HTML).
551
+ * Never all three: a block hidden everywhere is a delete. Bands: see `BREAKPOINTS`.
552
+ */
553
+ hideOn?: Array<"mobile" | "tablet" | "desktop">;
499
554
  }
500
555
  /** The paint a button swaps to on hover: fill, text colour, and the all-round border. */
501
556
  interface BlockHoverStyle {
@@ -504,6 +559,47 @@ interface BlockHoverStyle {
504
559
  textColor?: ColorToken;
505
560
  border?: boolean;
506
561
  }
562
+ /**
563
+ * Both bounds are INSTANTS: ISO 8601 with an offset, stored normalized to UTC seconds
564
+ * (`2026-11-27T00:00:00Z`). Shown from `from` (inclusive) until `until` (exclusive); a missing
565
+ * bound is open. An absent or null window is always shown.
566
+ */
567
+ interface VisibilityWindow {
568
+ from?: string;
569
+ until?: string;
570
+ }
571
+ type WindowState = "live" | "pending" | "expired";
572
+ /**
573
+ * A written window, normalized for storage, or the code it is refused with. null, undefined and
574
+ * an object with neither bound are all "no window". UTC at seconds precision makes the stored
575
+ * strings sort in time order, which the due scan (`list_due_window_projects`) relies on.
576
+ */
577
+ declare function normalizeWindow(value: unknown): {
578
+ window: VisibilityWindow | null;
579
+ } | {
580
+ error: "invalid_window" | "window_inverted";
581
+ };
582
+ /**
583
+ * Where `now` falls in a block's or Section's `window`. THE one reading of a window: every
584
+ * renderer, the delivery lanes and the in-place publish decide through it (or `inWindow`).
585
+ *
586
+ * `now` is an argument, never read from a clock here, so one request compares every block
587
+ * against the same instant. Bounds are compared as instants, so a stored offset cannot shift
588
+ * one. A bound that does not parse is read as open: a hand-edited row shows rather than vanishes.
589
+ */
590
+ declare function windowState(item: unknown, now: Date | number): WindowState;
591
+ /** Is the block or Section shown at `now`? No window is always shown. */
592
+ declare const inWindow: (item: unknown, now: Date | number) => boolean;
593
+ /**
594
+ * The blocks shown at `now`: every block outside its window removed, at any depth (a section's
595
+ * children, columns, slides, tab panels). What a published lane renders, collects from and
596
+ * delivers.
597
+ *
598
+ * Returns the SAME array when nothing is removed, so a tree with no window costs one walk and
599
+ * every consumer downstream is handed exactly what it was handed before windows existed.
600
+ * Non-array input is returned as it came.
601
+ */
602
+ declare function visibleBlocks<T>(blocks: T, now: Date | number): T;
507
603
  interface HeadingProps {
508
604
  text: string;
509
605
  level?: 1 | 2 | 3;
@@ -514,6 +610,8 @@ interface HeadingBlock {
514
610
  style?: BlockStyle;
515
611
  /** Editor-facing display name for this block's dock card. Not rendered. */
516
612
  label?: string;
613
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
614
+ window?: VisibilityWindow;
517
615
  props: HeadingProps;
518
616
  }
519
617
  interface TextProps {
@@ -527,6 +625,8 @@ interface TextBlock {
527
625
  style?: BlockStyle;
528
626
  /** Editor-facing display name for this block's dock card. Not rendered. */
529
627
  label?: string;
628
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
629
+ window?: VisibilityWindow;
530
630
  props: TextProps;
531
631
  }
532
632
  /**
@@ -597,6 +697,8 @@ interface RichTextBlock {
597
697
  style?: BlockStyle;
598
698
  /** Editor-facing display name for this block's dock card. Not rendered. */
599
699
  label?: string;
700
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
701
+ window?: VisibilityWindow;
600
702
  props: RichTextProps;
601
703
  }
602
704
  interface ImageProps {
@@ -612,6 +714,8 @@ interface ImageBlock {
612
714
  style?: BlockStyle;
613
715
  /** Editor-facing display name for this block's dock card. Not rendered. */
614
716
  label?: string;
717
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
718
+ window?: VisibilityWindow;
615
719
  props: ImageProps;
616
720
  }
617
721
  interface ButtonProps {
@@ -629,6 +733,8 @@ interface ButtonProps {
629
733
  icon?: ButtonIcon | null;
630
734
  /** Open the link in a new tab (`target="_blank" rel="noopener noreferrer"`). Absent ⇒ false. */
631
735
  newTab?: boolean;
736
+ /** `nofollow`, `sponsored` or `ugc`; joins the anchor's rel. Absent ⇒ none. */
737
+ rel?: "nofollow" | "sponsored" | "ugc";
632
738
  }
633
739
  interface ButtonBlock {
634
740
  type: "button";
@@ -636,6 +742,8 @@ interface ButtonBlock {
636
742
  style?: BlockStyle;
637
743
  /** Editor-facing display name for this block's dock card. Not rendered. */
638
744
  label?: string;
745
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
746
+ window?: VisibilityWindow;
639
747
  props: ButtonProps;
640
748
  }
641
749
  interface SpacerProps {
@@ -647,6 +755,8 @@ interface SpacerBlock {
647
755
  style?: BlockStyle;
648
756
  /** Editor-facing display name for this block's dock card. Not rendered. */
649
757
  label?: string;
758
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
759
+ window?: VisibilityWindow;
650
760
  props: SpacerProps;
651
761
  }
652
762
  interface ColumnsProps {
@@ -659,6 +769,8 @@ interface ColumnsBlock {
659
769
  style?: BlockStyle;
660
770
  /** Editor-facing display name for this block's dock card. Not rendered. */
661
771
  label?: string;
772
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
773
+ window?: VisibilityWindow;
662
774
  props: ColumnsProps;
663
775
  }
664
776
  interface VideoProps {
@@ -679,6 +791,8 @@ interface VideoBlock {
679
791
  style?: BlockStyle;
680
792
  /** Editor-facing display name for this block's dock card. Not rendered. */
681
793
  label?: string;
794
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
795
+ window?: VisibilityWindow;
682
796
  props: VideoProps;
683
797
  }
684
798
  interface FormProps {
@@ -690,6 +804,8 @@ interface FormBlock {
690
804
  style?: BlockStyle;
691
805
  /** Editor-facing display name for this block's dock card. Not rendered. */
692
806
  label?: string;
807
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
808
+ window?: VisibilityWindow;
693
809
  props: FormProps;
694
810
  }
695
811
  interface SectionProps {
@@ -704,6 +820,8 @@ interface SectionBlock {
704
820
  style?: BlockStyle;
705
821
  /** Editor-facing display name for this block's dock card. Not rendered. */
706
822
  label?: string;
823
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
824
+ window?: VisibilityWindow;
707
825
  props: SectionProps;
708
826
  }
709
827
  interface NavLink {
@@ -730,6 +848,8 @@ interface NavbarBlock {
730
848
  style?: BlockStyle;
731
849
  /** Editor-facing display name for this block's dock card. Not rendered. */
732
850
  label?: string;
851
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
852
+ window?: VisibilityWindow;
733
853
  props: NavbarProps;
734
854
  }
735
855
  interface FooterColumn {
@@ -746,6 +866,8 @@ interface FooterBlock {
746
866
  style?: BlockStyle;
747
867
  /** Editor-facing display name for this block's dock card. Not rendered. */
748
868
  label?: string;
869
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
870
+ window?: VisibilityWindow;
749
871
  props: FooterProps;
750
872
  }
751
873
  interface SlideItem {
@@ -764,6 +886,8 @@ interface SliderBlock {
764
886
  style?: BlockStyle;
765
887
  /** Editor-facing display name for this block's dock card. Not rendered. */
766
888
  label?: string;
889
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
890
+ window?: VisibilityWindow;
767
891
  props: SliderProps;
768
892
  }
769
893
  interface TabItem {
@@ -780,6 +904,8 @@ interface TabsBlock {
780
904
  style?: BlockStyle;
781
905
  /** Editor-facing display name for this block's dock card. Not rendered. */
782
906
  label?: string;
907
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
908
+ window?: VisibilityWindow;
783
909
  props: TabsProps;
784
910
  }
785
911
  interface ComponentProps {
@@ -817,6 +943,8 @@ interface ComponentBlock {
817
943
  style?: BlockStyle;
818
944
  /** Editor-facing display name for this block's dock card. Not rendered. */
819
945
  label?: string;
946
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
947
+ window?: VisibilityWindow;
820
948
  props: ComponentProps;
821
949
  }
822
950
  interface CollectionProps {
@@ -832,6 +960,29 @@ interface CollectionProps {
832
960
  /** "newest" (default) reverses the reader's created_at ordering. */
833
961
  order?: "newest" | "oldest";
834
962
  emptyText?: string;
963
+ /**
964
+ * List the published entries of ANOTHER model, on any page ("latest posts" on the home page).
965
+ * Absent: the entries of the page this block sits on, as above. A model of another project or
966
+ * workspace lists nothing. A sourced block is always a card list, on a detail URL too.
967
+ */
968
+ source?: {
969
+ modelId: string;
970
+ };
971
+ /** Sourced lists only. The Delivery API's filter grammar; field keys are the model's own. */
972
+ filters?: CollectionFilter[];
973
+ /** Sourced lists only. The Delivery API's sort grammar (`-publishedAt,title`); wins over `order`. */
974
+ sort?: string;
975
+ /** Sourced lists only. Entry ids rendered first, in this order; counted against `limit`. */
976
+ pinnedIds?: string[];
977
+ /** Sourced lists only. On an entry's detail URL, leave that entry out ("related posts"). */
978
+ excludeCurrent?: boolean;
979
+ }
980
+ interface CollectionFilter {
981
+ /** A field key of the source model, or `slug`, `publishedAt`, `locale`. */
982
+ field: string;
983
+ op: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "contains" | "in";
984
+ /** `in` takes a comma-separated list. */
985
+ value: string | number | boolean;
835
986
  }
836
987
  interface CollectionBlock {
837
988
  type: "collection";
@@ -839,6 +990,8 @@ interface CollectionBlock {
839
990
  style?: BlockStyle;
840
991
  /** Editor-facing display name for this block's dock card. Not rendered. */
841
992
  label?: string;
993
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
994
+ window?: VisibilityWindow;
842
995
  props: CollectionProps;
843
996
  }
844
997
  type ContentBlock = CollectionBlock | HeadingBlock | TextBlock | RichTextBlock | ImageBlock | ButtonBlock | SpacerBlock | ColumnsBlock | VideoBlock | FormBlock | SectionBlock | NavbarBlock | FooterBlock | SliderBlock | TabsBlock | ComponentBlock;
@@ -1034,6 +1187,30 @@ declare function componentInstanceAddresses(instanceBlockId: string, props: read
1034
1187
  * render-page.ts. Unknown tokens are dropped — closed set, no arbitrary CSS.
1035
1188
  */
1036
1189
 
1190
+ /**
1191
+ * Where a block's `hideOn` bands start and end: mobile at most 767px, tablet 768-1023px, desktop
1192
+ * 1024px and up (the dock's 390 / 810 / 1280 canvases fall one in each band). A site's CSS
1193
+ * carries these numbers, so changing one shifts every published site's media queries.
1194
+ */
1195
+ declare const BREAKPOINTS: {
1196
+ readonly mobile: 767;
1197
+ readonly tablet: 1023;
1198
+ };
1199
+ /**
1200
+ * The ONE stylesheet behind `data-bcms-hide="mobile tablet"`. `!important` because a block's own
1201
+ * inline `display` (`fullHeight` writes `display:flex`) would otherwise beat it. `display:none`
1202
+ * also takes the block out of the accessibility tree at that width. Hosted pages inline it in
1203
+ * the head, `<BcmsBlocks>` emits it when a block uses `hideOn`, and a site that renders its own
1204
+ * markup puts it in its global CSS.
1205
+ */
1206
+ declare const RESPONSIVE_CSS: string;
1207
+ /**
1208
+ * The `data-bcms-hide` value for a block style (`"mobile tablet"`), or "" when it hides nowhere.
1209
+ * Closed set, fixed order, so a hand-edited row cannot put anything else into an attribute.
1210
+ */
1211
+ declare function hideOnAttr(style: unknown): string;
1212
+ /** True when any block in the tree hides somewhere; lets a lane skip `RESPONSIVE_CSS` when unused. */
1213
+ declare function usesHideOn(blocks: unknown): boolean;
1037
1214
  /**
1038
1215
  * 🔴 The ONE place a free-form string is allowed near CSS: `bgImage` is author-supplied, so it is
1039
1216
  * rejected — never escaped — unless it is an http(s) or root-relative url with nothing that could
@@ -1088,7 +1265,7 @@ declare function headingPaintCss(style: BlockStyle | undefined, opts?: {
1088
1265
  * painted a box BEHIND the anchor, which kept its variant's own fill. 🔴 Twin of `PLACEMENT_KEYS`
1089
1266
  * in src/lib/sites/render-page.ts; src/__tests__/sites/block-style-parity.test.ts pins the two.
1090
1267
  */
1091
- declare const BUTTON_PLACEMENT_KEYS: readonly ["align", "contentAlign", "contentWidth", "marginTop", "marginBottom", "fullHeight", "spaceTop", "spaceBottom"];
1268
+ declare const BUTTON_PLACEMENT_KEYS: readonly ["align", "contentAlign", "contentWidth", "marginTop", "marginBottom", "fullHeight", "spaceTop", "spaceBottom", "hideOn"];
1092
1269
  /**
1093
1270
  * A button's style split in two: `placement` (the keys its wrapper keeps — feed it to
1094
1271
  * `blockStyleToCss` / `blockStyleToInnerCss` exactly like any block's style) and `paint`, the
@@ -1179,6 +1356,10 @@ interface LayoutFieldDefinition {
1179
1356
  helpSource?: "auto" | "human" | "agent";
1180
1357
  /** `true`: no route the last release inspected renders this field — hidden, never deleted. `false`: a person keeps it shown. */
1181
1358
  stale?: boolean;
1359
+ /** false: a page may not override this value; the Global value renders on every page. Absent = true. */
1360
+ overridable?: boolean;
1361
+ /** Shown where the page editor would have offered the field. Required when overridable is false. */
1362
+ lockReason?: string;
1182
1363
  }
1183
1364
  /** The canonical public input contract all variants in a group implement. */
1184
1365
  type CanonicalInputDefinition = LayoutFieldDefinition;
@@ -1242,6 +1423,11 @@ interface LayoutSection {
1242
1423
  locale?: string;
1243
1424
  /** The stable id of the base Section this variant replaces on routes in `locale`. */
1244
1425
  variantOf?: string;
1426
+ /**
1427
+ * When the Section is shown (an announcement bar, a seasonal band). Absent = always. It is the
1428
+ * SLOT's: a locale variant shows when its base does, whatever the variant's own row says.
1429
+ */
1430
+ window?: VisibilityWindow;
1245
1431
  }
1246
1432
  interface LayoutStructureDocument {
1247
1433
  version: 1;
@@ -1379,6 +1565,7 @@ type ManagementLayoutCommand = {
1379
1565
  name: string;
1380
1566
  icon: LayoutSectionIcon;
1381
1567
  zone: "before_page" | "after_page";
1568
+ window?: VisibilityWindow;
1382
1569
  } | {
1383
1570
  type: "restore-section";
1384
1571
  section: LayoutSection;
@@ -1395,6 +1582,7 @@ type ManagementLayoutCommand = {
1395
1582
  zone?: LayoutZone;
1396
1583
  orderKey?: string;
1397
1584
  description?: string;
1585
+ window?: VisibilityWindow | null;
1398
1586
  } | {
1399
1587
  type: "remove-section";
1400
1588
  sectionId: string;
@@ -1468,6 +1656,8 @@ interface DeliveredLayout {
1468
1656
  */
1469
1657
  locale?: string;
1470
1658
  variantOf?: string;
1659
+ /** Present only on a Section that has one. A published read never delivers a Section outside it. */
1660
+ window?: VisibilityWindow;
1471
1661
  fields: Record<string, unknown>;
1472
1662
  items: Array<{
1473
1663
  id: string;
@@ -1501,6 +1691,19 @@ interface DeliveredLayout {
1501
1691
  }>;
1502
1692
  }>;
1503
1693
  }
1694
+ /**
1695
+ * A Layout structure as a visitor is served it at `now`: every Section outside its window
1696
+ * removed, and with a removed base its locale variants (a variant only ever renders in its base's
1697
+ * slot). The same document when nothing is removed, and anything that is not one as it came.
1698
+ */
1699
+ declare function visibleLayoutStructure<T>(structure: T, now: Date | number): T;
1700
+ /**
1701
+ * A delivered Layout as a visitor is served it at `now`: the nodes whose Section is outside its
1702
+ * window removed, with their `sections` entries and any variant delivered beside a removed base.
1703
+ * Applied AFTER any cache, so a cached Layout never serves a Section past its boundary. The same
1704
+ * object when nothing is removed.
1705
+ */
1706
+ declare function visibleDeliveredLayout<T extends DeliveredLayout | null | undefined>(layout: T, now: Date | number): T;
1504
1707
 
1505
1708
  /**
1506
1709
  * Component — a reusable, Webflow-style block tree (a "symbol").
@@ -1555,7 +1758,7 @@ type ComponentCategory = "navbar" | "footer" | "button" | "section" | "slider" |
1555
1758
  *
1556
1759
  * Order is not semantic; append rather than re-sort, so a diff stays readable.
1557
1760
  */
1558
- declare const COMPONENT_PROP_TYPES: readonly ["text", "richtext", "image", "url", "boolean", "number", "select", "group", "table", "slot", "form"];
1761
+ declare const COMPONENT_PROP_TYPES: readonly ["text", "richtext", "image", "url", "boolean", "number", "select", "group", "table", "slot", "form", "link", "button", "date", "color", "file", "icon", "multiselect"];
1559
1762
  type ComponentPropType = (typeof COMPONENT_PROP_TYPES)[number];
1560
1763
  /**
1561
1764
  * The value a `form` prop STORES: just the id. The form itself is a Form document and stays the
@@ -1586,6 +1789,11 @@ interface ComponentPropDef {
1586
1789
  * `{ componentId, overrides }` and its `target.path` is `props`, so filling a slot writes
1587
1790
  * that object over an empty `component` block's props.
1588
1791
  *
1792
+ * With `config.multiple` it holds a LIST of them and targets a container's child list
1793
+ * (`props.children` on a `section`, `props.columns.<n>` on a `columns`): the resolver writes
1794
+ * one `component` block per item there, so every renderer draws them as ordinary children.
1795
+ * @see slotItems
1796
+ *
1589
1797
  * On a PAGE a slot fill is a LIVE reference; a published ENTRY freezes it (resolveDeep).
1590
1798
  */
1591
1799
  /**
@@ -1597,6 +1805,14 @@ interface ComponentPropDef {
1597
1805
  * select → one string from `config.options`
1598
1806
  * group → an object keyed by `config.fields`
1599
1807
  * table → an array of such objects (slider.slides)
1808
+ * link → an href string (URL, internal path or #fragment)
1809
+ * button → ONE `ButtonValue` (button-look.ts): label, href and look as a unit, never a text prop plus
1810
+ * a url prop that drift apart. Targets the `props` of a `button` block.
1811
+ * date → `YYYY-MM-DD`
1812
+ * color → a hex colour (`#RGB`, `#RRGGBB`, `#RRGGBBAA`)
1813
+ * file → `{ url, name?, size? }`, or a bare URL string
1814
+ * icon → a BetterCMS icon name (`DOCK_ICON_NAMES`), or an uploaded icon as an image value
1815
+ * multiselect → a `string[]`, each one of `config.options`
1600
1816
  *
1601
1817
  * `reference` is absent on purpose: nothing resolves a content-entry id at component render
1602
1818
  * time, so such a prop would save cleanly and render as nothing.
@@ -1619,12 +1835,28 @@ interface ComponentPropDef {
1619
1835
  helpSource?: "auto" | "human" | "agent";
1620
1836
  /** Ghost text inside a text-ish control. */
1621
1837
  placeholder?: string;
1838
+ /**
1839
+ * Editor-only conditional visibility: show this prop's control only while sibling props (by
1840
+ * key) hold these values. The save schema refuses a key no prop of the component declares.
1841
+ */
1842
+ showIf?: ShowIf;
1843
+ /**
1844
+ * The content-model field's `readOnly` / `lockReason` pair, as STORED MARKERS only: the
1845
+ * editor may render the prop read-only, and no write of a prop value is refused for them.
1846
+ */
1847
+ readOnly?: true | "developer";
1848
+ lockReason?: string;
1622
1849
  /** Per-type settings; the save schema enforces that each is present on the type needing it. */
1623
1850
  config?: {
1624
1851
  /** `slot` — a pick allowlist. */
1625
1852
  componentIds?: string[];
1853
+ /** `slot` — the value is a list of instances, not one. @see slotItems */
1854
+ multiple?: boolean;
1855
+ /** `slot` with `multiple` — bounds on the list. `maxItems` defaults to {@link SLOT_MAX_ITEMS}. */
1856
+ minItems?: number;
1857
+ maxItems?: number;
1626
1858
  /**
1627
- * `select` — the choices. Required, and a default must be one of them. `{ label, value }`
1859
+ * `select` / `multiselect` — the choices. Required, and a default must be one of them. `{ label, value }`
1628
1860
  * when the stored value is not what an author should read (a component id, for instance).
1629
1861
  */
1630
1862
  options?: Array<string | {
@@ -1708,6 +1940,34 @@ interface ComponentSubField {
1708
1940
  options?: string[];
1709
1941
  } & Record<string, unknown>;
1710
1942
  }
1943
+ /** The value a `slot` prop holds — the draft half of a `component-ref`, deliberately. */
1944
+ interface SlotValue {
1945
+ componentId: string;
1946
+ overrides?: Record<string, unknown>;
1947
+ }
1948
+ /**
1949
+ * One child of a `multiple` slot: a {@link SlotValue} that may also carry the two placement keys a
1950
+ * `component` block has. The child block is built with them, so it hides per breakpoint and
1951
+ * shows inside its window exactly as a block placed by hand does.
1952
+ */
1953
+ interface SlotItem extends SlotValue {
1954
+ style?: BlockStyle;
1955
+ window?: VisibilityWindow;
1956
+ }
1957
+ /** How many children a `multiple` slot holds when its prop sets no `config.maxItems`. */
1958
+ declare const SLOT_MAX_ITEMS = 12;
1959
+ /** The most `config.maxItems` may ask for: one placement never expands into more blocks than this. */
1960
+ declare const SLOT_ITEMS_CAP = 50;
1961
+ /**
1962
+ * A slot value as a list of its instances, whichever shape it was stored in: `[]` for nothing,
1963
+ * one item for a single `{ componentId, overrides }`, the array's own items for a `multiple`
1964
+ * slot. An item with no `componentId` names no component and is left out.
1965
+ *
1966
+ * THE one reading of a slot value. A walker that tests `value.componentId` alone sees a
1967
+ * `multiple` slot as empty, and for a "where used" answer or a delete guard that is a component
1968
+ * removed from under the pages that hold it.
1969
+ */
1970
+ declare function slotItems(value: unknown): SlotItem[];
1711
1971
  /**
1712
1972
  * A component is a DRAFT until published; only the published copy reaches a visitor.
1713
1973
  * Mirrors pages and entries. See migration 0186_component_draft_live_split.sql.
@@ -2201,7 +2461,7 @@ type FormStatus = "draft" | "published";
2201
2461
  interface FormField {
2202
2462
  key: string;
2203
2463
  label: string;
2204
- type: "text" | "email" | "textarea" | "select" | "checkbox" | "checkboxes" | "radio" | "number" | "phone" | "date" | "url" | "consent" | "hidden";
2464
+ type: "text" | "email" | "textarea" | "select" | "checkbox" | "checkboxes" | "radio" | "number" | "phone" | "date" | "url" | "consent" | "hidden" | "step";
2205
2465
  placeholder?: string;
2206
2466
  helpText?: string;
2207
2467
  required?: boolean;
@@ -2587,4 +2847,4 @@ declare function regionOf<E extends {
2587
2847
  el: E;
2588
2848
  }[], el: unknown): string | null;
2589
2849
 
2590
- export { type AlignToken, type ApiError, type ApiKey, type ApiKeyPermission, type ApiKeyTokenType, type ArrayZoneConfig, type AssertEqual, type AuthResponse, type AuthSession, type AuthUser, BLOCK_BINDINGS, BUTTON_ICONS, BUTTON_ICON_NAMES, BUTTON_LOOK_KEYS, BUTTON_PLACEMENT_KEYS, BUTTON_SIZES, BUTTON_VARIANTS, type BetterCMSErrorCode, type BgToken, type BindingKind, type BlockBinding, type BlockHoverStyle, type BlockStyle, type BlockType, type ButtonBlock, type ButtonIcon, type ButtonLinkCandidate, type ButtonLookKey, type ButtonProps, type ButtonSizeName, type ButtonValue, type ButtonVariant, COMPONENT_PROP_TYPES, type CanonicalInputDefinition, type CollectionBlock, type CollectionProps, type ColorToken, type ColumnsBlock, type ColumnsProps, type Component, type ComponentBlock, type ComponentCategory, type ComponentFormPropRef, type ComponentPropDef, type ComponentPropType, type ComponentPropUi, type ComponentProps, type ComponentSubField, type ComponentVariantGroup, type Content, type ContentBlock, type ContentEntry, type ContentModel, type ContentModelField, type ContentModelFieldType, type ContentModelFieldUi, type ContentPageResult, type ContentResponse, type ContentWidthToken, type CornerToken, DIALOG_SELECTOR, DOCK_BUDGET, DOCK_ICON_NAMES, DOCK_ICON_NAME_SET, type DeepReadonly, type DeliveredLayout, type DeliveryComponent, type DeliveryEntry, type DeliveryEntrySeo, type DeliveryList, type DeliveryPage, type DerivedLocator, type DockBudget, type DockFieldLike, type DockFieldsetLike, type DockIconName, type DockInputRef, type DockLane, type DockNode, type DockPropGroupLike, type DockScreen, type ElementLike, type ExpandedBinding, FIELD_STYLE_KEYS, FOOTER_SECTION_ID, type FieldConfig, type FieldStyle, type FieldStyleKey, type FieldStyles, type Fieldset, type FieldsetKind, type FieldsetUiBefore, type FontSizeToken, type FooterBlock, type FooterColumn, type FooterProps, type Form, type FormBlock, type FormField, type FormProps, type FormSubmission, type HeadingBlock, type HeadingProps, type ImageBlock, type ImageProps, LANDMARK_SELECTOR, LAYOUT_SECTION_ICONS, LAYOUT_SECTION_ICON_SET, type LandmarkChainElement, type LandmarkElement, type LayoutComponentItem, type LayoutDataDocument, type LayoutFieldDefinition, type LayoutFieldItem, type LayoutFieldType, type LayoutScalarFieldType, type LayoutSection, type LayoutSectionIcon, type LayoutSectionItem, type LayoutStructureDocument, type LayoutZone, type LocatorGrammar, MAIN_SELECTOR, type ManagementLayoutCommand, type MediaAsset, type MediaCaptionTrack, type MediaFieldValue, NAVIGATION_SECTION_ID, type NavLink, type NavbarBlock, type NavbarProps, PAGE_CONTENT_ANCHOR_ID, type Page, type PageLayoutOverrideDataDocument, type PageLayoutOverrideDocument, type PageLayoutOverrideState, type PageLayoutOverrideStructureDocument, type PageLayoutSectionOverride, type PageLayoutSectionStructureOverride, type PageMetaJson, type PageOnlyLayoutSection, type PageSectionPlacement, type PaginatedResult, type Perspective, type PortableTextDocument, type PortableTextNode, type PropGroup, type RadiusToken, type Redirect, type ReservedLayoutSection, type ResolvedLayoutNode, type RichTextBlock, type RichTextProps, SECTION_DOCTRINE, SECTION_SPACE_TOKENS, SPACE_BOTTOM_TOKENS, SPACE_STEP_TOKENS, type SectionBlock, type SectionProps, type SectionSpaceToken, type ShadowToken, type SignInInput, type SignUpInput, type SiteSeoDefaults, type SlideItem, type SliderBlock, type SliderProps, type SpaceStepToken, type SpaceToken, type SpacerBlock, type SpacerProps, type TabItem, type TabsBlock, type TabsProps, type TextBlock, type TextProps, type ThemeToken, type UiBefore, type UiOrigin, type VariantInputMapping, type VideoBlock, type VideoProps, type WeightToken, type Workspace, type ZoneField, blockStyleToColumnCss, blockStyleToCss, blockStyleToInnerCss, buttonIconOf, buttonIconSvg, buttonLinkPairs, buttonValueOfInstance, buttonValueProblem, buttonVariantOf, candidateRole, componentInstanceAddresses, componentPropTargetKey, derivedPairs, dockBudget, dockScreens, expandBlockBindings, fieldStyleCssText, fieldStyleToCss, formatPropsAttribute, getBlockType, headingPaintCss, isBlock, isChromeLandmark, keyWords, landmarkRegions, locatorFor, parsePropsAttribute, pickFieldStyle, readButtonValue, readPath, regionOf, safeCssUrl, spaceTokenCss, splitButtonStyle, withButtonLookProps };
2850
+ export { type AlignToken, type ApiError, type ApiKey, type ApiKeyPermission, type ApiKeyTokenType, type ArrayZoneConfig, type AssertEqual, type AuthResponse, type AuthSession, type AuthUser, BLOCK_BINDINGS, BREAKPOINTS, BUTTON_ICONS, BUTTON_ICON_NAMES, BUTTON_LOOK_KEYS, BUTTON_PLACEMENT_KEYS, BUTTON_SIZES, BUTTON_VARIANTS, type BetterCMSErrorCode, type BgToken, type BindingKind, type BlockBinding, type BlockHoverStyle, type BlockStyle, type BlockType, type ButtonBlock, type ButtonIcon, type ButtonLinkCandidate, type ButtonLookKey, type ButtonProps, type ButtonSizeName, type ButtonValue, type ButtonVariant, COMPONENT_PROP_TYPES, type CanonicalInputDefinition, type CollectionBlock, type CollectionFilter, type CollectionProps, type ColorToken, type ColumnsBlock, type ColumnsProps, type Component, type ComponentBlock, type ComponentCategory, type ComponentFormPropRef, type ComponentPropDef, type ComponentPropType, type ComponentPropUi, type ComponentProps, type ComponentSubField, type ComponentVariantGroup, type Content, type ContentBlock, type ContentEntry, type ContentModel, type ContentModelField, type ContentModelFieldType, type ContentModelFieldUi, type ContentPageResult, type ContentResponse, type ContentWidthToken, type CornerToken, DIALOG_SELECTOR, DOCK_BUDGET, DOCK_ICON_NAMES, DOCK_ICON_NAME_SET, type DeepReadonly, type DeliveredLayout, type DeliveryComponent, type DeliveryEntry, type DeliveryEntrySeo, type DeliveryList, type DeliveryPage, type DerivedLocator, type DockBudget, type DockFieldLike, type DockFieldsetLike, type DockIconName, type DockInputRef, type DockLane, type DockNode, type DockPropGroupLike, type DockScreen, type ElementLike, type ExpandedBinding, FIELD_STYLE_KEYS, FOOTER_SECTION_ID, type FieldConfig, type FieldStyle, type FieldStyleKey, type FieldStyles, type Fieldset, type FieldsetKind, type FieldsetUiBefore, type FontSizeToken, type FooterBlock, type FooterColumn, type FooterProps, type Form, type FormBlock, type FormField, type FormProps, type FormSubmission, type HeadingBlock, type HeadingProps, type ImageBlock, type ImageProps, LANDMARK_SELECTOR, LAYOUT_SECTION_ICONS, LAYOUT_SECTION_ICON_SET, LINK_RELS, type LandmarkChainElement, type LandmarkElement, type LayoutComponentItem, type LayoutDataDocument, type LayoutFieldDefinition, type LayoutFieldItem, type LayoutFieldType, type LayoutScalarFieldType, type LayoutSection, type LayoutSectionIcon, type LayoutSectionItem, type LayoutStructureDocument, type LayoutZone, type LinkRel, type LocatorGrammar, MAIN_SELECTOR, type ManagementLayoutCommand, type MediaAsset, type MediaCaptionTrack, type MediaFieldValue, NAVIGATION_SECTION_ID, type NavLink, type NavbarBlock, type NavbarProps, PAGE_CONTENT_ANCHOR_ID, type Page, type PageLayoutOverrideDataDocument, type PageLayoutOverrideDocument, type PageLayoutOverrideState, type PageLayoutOverrideStructureDocument, type PageLayoutSectionOverride, type PageLayoutSectionStructureOverride, type PageMetaJson, type PageOnlyLayoutSection, type PageSectionPlacement, type PaginatedResult, type Perspective, type PortableTextDocument, type PortableTextNode, type PropGroup, RESPONSIVE_CSS, type RadiusToken, type ReadButtonValue, type Redirect, type ReservedLayoutSection, type ResolvedLayoutNode, type RichTextBlock, type RichTextProps, SECTION_DOCTRINE, SECTION_SPACE_TOKENS, SLOT_ITEMS_CAP, SLOT_MAX_ITEMS, SPACE_BOTTOM_TOKENS, SPACE_STEP_TOKENS, type SectionBlock, type SectionProps, type SectionSpaceToken, type ShadowToken, type ShowIf, type ShowIfRule, type SignInInput, type SignUpInput, type SiteSeoDefaults, type SlideItem, type SliderBlock, type SliderProps, type SlotItem, type SlotValue, type SpaceStepToken, type SpaceToken, type SpacerBlock, type SpacerProps, type TabItem, type TabsBlock, type TabsProps, type TextBlock, type TextProps, type ThemeToken, type UiBefore, type UiOrigin, type VariantInputMapping, type VideoBlock, type VideoProps, type VisibilityWindow, type WeightToken, type WindowState, type Workspace, type ZoneField, anchorTargetRel, blockStyleToColumnCss, blockStyleToCss, blockStyleToInnerCss, buttonBlockProps, buttonIconOf, buttonIconSvg, buttonLinkPairs, buttonValueOfInstance, buttonValueProblem, buttonVariantOf, candidateRole, componentInstanceAddresses, componentPropTargetKey, derivedPairs, dockBudget, dockScreens, expandBlockBindings, fieldStyleCssText, fieldStyleToCss, formatPropsAttribute, getBlockType, headingPaintCss, hideOnAttr, inWindow, isBlock, isChromeLandmark, keyWords, landmarkRegions, locatorFor, normalizeWindow, parsePropsAttribute, pickFieldStyle, readButtonValue, readPath, regionOf, safeCssUrl, slotItems, spaceTokenCss, splitButtonStyle, usesHideOn, visibleBlocks, visibleDeliveredLayout, visibleLayoutStructure, windowState, withButtonLookProps };