ferst-core 0.7.7 → 0.7.8

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/content/blocks.ts CHANGED
@@ -773,7 +773,30 @@ export type LeafBlock =
773
773
  | z.infer<typeof listBlock>;
774
774
 
775
775
  /** The full recursive block type: leaf primitives + layout containers. */
776
- export type Block = LeafBlock | RecipeBlock | SectionBlock | GridBlock | StackBlock;
776
+ /**
777
+ * Alignment overrides every block takes, so a page's look can follow a client's preference
778
+ * from the page data alone. Defaults are the sensible visual choice (a block follows its
779
+ * page / section; a recipe's section title is centred).
780
+ * - `align`: re-aligns the whole block — title, intro, text, and the icons, pills and
781
+ * buttons in its cards — overriding the page / section for this one block.
782
+ * - `headingAlign`: just a recipe's title + intro (e.g. a start-aligned title over centred
783
+ * cards). Recipes without a section title ignore it.
784
+ * `heading`, `banner` and `section` already had `align` with this meaning and keep it.
785
+ */
786
+ const alignOption = z.enum(['start', 'center', 'end']);
787
+ export interface BlockLayout {
788
+ align?: 'start' | 'center' | 'end';
789
+ headingAlign?: 'start' | 'center' | 'end';
790
+ }
791
+ function withLayout<T extends z.AnyZodObject>(block: T): z.AnyZodObject {
792
+ const shape = block.shape as Record<string, unknown>;
793
+ return block.extend({
794
+ ...('align' in shape ? {} : { align: alignOption.optional() }),
795
+ headingAlign: alignOption.optional(),
796
+ });
797
+ }
798
+
799
+ export type Block = (LeafBlock | RecipeBlock | SectionBlock | GridBlock | StackBlock) & BlockLayout;
777
800
  export type BlockType = Block['type'];
778
801
 
779
802
  export const sectionBlock = z.object({
@@ -806,54 +829,57 @@ export const stackBlock = z.object({
806
829
  * a member without a matching BlockRenderer registry entry is a TypeScript error
807
830
  * (the exhaustiveness guard against a silent-undefined render crash). Cast to
808
831
  * the recursive `Block` type because `z.lazy` fields can't be inferred. */
809
- export const blockSchema = z.discriminatedUnion('type', [
810
- headingBlock,
811
- proseBlock,
812
- imageBlock,
813
- buttonBlock,
814
- quoteBlock,
815
- dividerBlock,
816
- spacerBlock,
817
- badgeBlock,
818
- statBlock,
819
- listBlock,
820
- heroBlock,
821
- featureCardsBlock,
822
- ctaBlock,
823
- faqBlock,
824
- galleryBlock,
825
- testimonialBlock,
826
- contactFormBlock,
827
- formBlock,
828
- accordionBlock,
829
- tilesBlock,
830
- bannerBlock,
831
- statsBlock,
832
- stepsBlock,
833
- pricingBlock,
834
- logosBlock,
835
- tabsBlock,
836
- announcementBlock,
837
- bentoBlock,
838
- marqueeBlock,
839
- latestPostsBlock,
840
- mapEmbedBlock,
841
- tableBlock,
842
- noticeBlock,
843
- mediaCardsBlock,
844
- contactCardBlock,
845
- locationsBlock,
846
- videoBlock,
847
- documentsBlock,
848
- linkRowBlock,
849
- documentBlock,
850
- timetableBlock,
851
- contactsBlock,
852
- mediaTextBlock,
853
- sectionBlock,
854
- gridBlock,
855
- stackBlock,
856
- ]) as unknown as z.ZodType<Block>;
832
+ export const blockSchema = z.discriminatedUnion(
833
+ 'type',
834
+ [
835
+ headingBlock,
836
+ proseBlock,
837
+ imageBlock,
838
+ buttonBlock,
839
+ quoteBlock,
840
+ dividerBlock,
841
+ spacerBlock,
842
+ badgeBlock,
843
+ statBlock,
844
+ listBlock,
845
+ heroBlock,
846
+ featureCardsBlock,
847
+ ctaBlock,
848
+ faqBlock,
849
+ galleryBlock,
850
+ testimonialBlock,
851
+ contactFormBlock,
852
+ formBlock,
853
+ accordionBlock,
854
+ tilesBlock,
855
+ bannerBlock,
856
+ statsBlock,
857
+ stepsBlock,
858
+ pricingBlock,
859
+ logosBlock,
860
+ tabsBlock,
861
+ announcementBlock,
862
+ bentoBlock,
863
+ marqueeBlock,
864
+ latestPostsBlock,
865
+ mapEmbedBlock,
866
+ tableBlock,
867
+ noticeBlock,
868
+ mediaCardsBlock,
869
+ contactCardBlock,
870
+ locationsBlock,
871
+ videoBlock,
872
+ documentsBlock,
873
+ linkRowBlock,
874
+ documentBlock,
875
+ timetableBlock,
876
+ contactsBlock,
877
+ mediaTextBlock,
878
+ sectionBlock,
879
+ gridBlock,
880
+ stackBlock,
881
+ ].map(withLayout) as unknown as [z.AnyZodObject, z.AnyZodObject, ...z.AnyZodObject[]],
882
+ ) as unknown as z.ZodType<Block>;
857
883
 
858
884
  /** A page = metadata + an ordered list of blocks. Stored as JSON in the client
859
885
  * repo; the AI composes it, a human verifies. This is also the schema of the