ferst-core 0.7.7 → 0.7.9
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/bin/ferst-modules.mjs +18 -2
- package/components/BlockRenderer.astro +125 -119
- package/components/Button.astro +1 -1
- package/components/CalendarEmbed.astro +2 -1
- package/components/Card.astro +2 -0
- package/components/SiteHeader.astro +24 -0
- package/components/recipes/Accordion.astro +91 -87
- package/components/recipes/Bento.astro +7 -3
- package/components/recipes/ContactForm.astro +6 -3
- package/components/recipes/Cta.astro +5 -1
- package/components/recipes/Faq.astro +5 -1
- package/components/recipes/FeatureCards.astro +115 -112
- package/components/recipes/Hero.astro +521 -495
- package/components/recipes/LatestPosts.astro +1 -1
- package/components/recipes/MediaCards.astro +214 -213
- package/components/recipes/Notice.astro +90 -90
- package/components/recipes/Pricing.astro +7 -4
- package/components/recipes/Stats.astro +155 -152
- package/components/recipes/Steps.astro +124 -121
- package/components/recipes/Tabs.astro +138 -134
- package/components/recipes/Tiles.astro +291 -287
- package/content/blocks.ts +88 -52
- package/layouts/Base.astro +496 -484
- package/lib/inline-markdown.ts +13 -0
- package/lib/slug.ts +1 -1
- package/package.json +1 -1
- package/routes/calendar/index.astro +2 -1
package/content/blocks.ts
CHANGED
|
@@ -146,9 +146,19 @@ export const heroBlock = z.object({
|
|
|
146
146
|
})
|
|
147
147
|
.optional(),
|
|
148
148
|
layout: z.enum(['centered', 'split', 'media']).default('centered'),
|
|
149
|
-
/** How much of the screen a `media` hero takes: `
|
|
150
|
-
*
|
|
151
|
-
|
|
149
|
+
/** How much of the screen a `media` hero takes: `compact` (a narrow band for a short,
|
|
150
|
+
* wide header strip, e.g. 1000 × 175), `standard` (a band), `tall`, or `screen` — the
|
|
151
|
+
* full first screen, edge to edge (e.g. a video opener). */
|
|
152
|
+
size: z.enum(['compact', 'standard', 'tall', 'screen']).default('standard'),
|
|
153
|
+
/** A `media` hero runs edge to edge across the screen, like a full-bleed banner
|
|
154
|
+
* (`screen` always does). */
|
|
155
|
+
fullBleed: z.boolean().default(false),
|
|
156
|
+
/** Which part of the background image shows when it's cropped: one or two positions,
|
|
157
|
+
* keywords or percentages (e.g. `center 30%`, `top`, `left 20%`). Default `center`. */
|
|
158
|
+
focus: z
|
|
159
|
+
.string()
|
|
160
|
+
.regex(/^(?:left|center|right|top|bottom|\d{1,3}%)(?: (?:left|center|right|top|bottom|\d{1,3}%))?$/, 'focus is one or two of left / center / right / top / bottom / a percentage, e.g. "center 30%"')
|
|
161
|
+
.optional(),
|
|
152
162
|
/** A `media` hero's background drifts more slowly than the page as it scrolls (any
|
|
153
163
|
* background: image, slides or video). Off on touch devices and under reduced motion. */
|
|
154
164
|
parallax: z.boolean().default(false),
|
|
@@ -773,7 +783,30 @@ export type LeafBlock =
|
|
|
773
783
|
| z.infer<typeof listBlock>;
|
|
774
784
|
|
|
775
785
|
/** The full recursive block type: leaf primitives + layout containers. */
|
|
776
|
-
|
|
786
|
+
/**
|
|
787
|
+
* Alignment overrides every block takes, so a page's look can follow a client's preference
|
|
788
|
+
* from the page data alone. Defaults are the sensible visual choice (a block follows its
|
|
789
|
+
* page / section; a recipe's section title is centred).
|
|
790
|
+
* - `align`: re-aligns the whole block — title, intro, text, and the icons, pills and
|
|
791
|
+
* buttons in its cards — overriding the page / section for this one block.
|
|
792
|
+
* - `headingAlign`: just a recipe's title + intro (e.g. a start-aligned title over centred
|
|
793
|
+
* cards). Recipes without a section title ignore it.
|
|
794
|
+
* `heading`, `banner` and `section` already had `align` with this meaning and keep it.
|
|
795
|
+
*/
|
|
796
|
+
const alignOption = z.enum(['start', 'center', 'end']);
|
|
797
|
+
export interface BlockLayout {
|
|
798
|
+
align?: 'start' | 'center' | 'end';
|
|
799
|
+
headingAlign?: 'start' | 'center' | 'end';
|
|
800
|
+
}
|
|
801
|
+
function withLayout<T extends z.AnyZodObject>(block: T): z.AnyZodObject {
|
|
802
|
+
const shape = block.shape as Record<string, unknown>;
|
|
803
|
+
return block.extend({
|
|
804
|
+
...('align' in shape ? {} : { align: alignOption.optional() }),
|
|
805
|
+
headingAlign: alignOption.optional(),
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export type Block = (LeafBlock | RecipeBlock | SectionBlock | GridBlock | StackBlock) & BlockLayout;
|
|
777
810
|
export type BlockType = Block['type'];
|
|
778
811
|
|
|
779
812
|
export const sectionBlock = z.object({
|
|
@@ -806,54 +839,57 @@ export const stackBlock = z.object({
|
|
|
806
839
|
* a member without a matching BlockRenderer registry entry is a TypeScript error
|
|
807
840
|
* (the exhaustiveness guard against a silent-undefined render crash). Cast to
|
|
808
841
|
* the recursive `Block` type because `z.lazy` fields can't be inferred. */
|
|
809
|
-
export const blockSchema = z.discriminatedUnion(
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
842
|
+
export const blockSchema = z.discriminatedUnion(
|
|
843
|
+
'type',
|
|
844
|
+
[
|
|
845
|
+
headingBlock,
|
|
846
|
+
proseBlock,
|
|
847
|
+
imageBlock,
|
|
848
|
+
buttonBlock,
|
|
849
|
+
quoteBlock,
|
|
850
|
+
dividerBlock,
|
|
851
|
+
spacerBlock,
|
|
852
|
+
badgeBlock,
|
|
853
|
+
statBlock,
|
|
854
|
+
listBlock,
|
|
855
|
+
heroBlock,
|
|
856
|
+
featureCardsBlock,
|
|
857
|
+
ctaBlock,
|
|
858
|
+
faqBlock,
|
|
859
|
+
galleryBlock,
|
|
860
|
+
testimonialBlock,
|
|
861
|
+
contactFormBlock,
|
|
862
|
+
formBlock,
|
|
863
|
+
accordionBlock,
|
|
864
|
+
tilesBlock,
|
|
865
|
+
bannerBlock,
|
|
866
|
+
statsBlock,
|
|
867
|
+
stepsBlock,
|
|
868
|
+
pricingBlock,
|
|
869
|
+
logosBlock,
|
|
870
|
+
tabsBlock,
|
|
871
|
+
announcementBlock,
|
|
872
|
+
bentoBlock,
|
|
873
|
+
marqueeBlock,
|
|
874
|
+
latestPostsBlock,
|
|
875
|
+
mapEmbedBlock,
|
|
876
|
+
tableBlock,
|
|
877
|
+
noticeBlock,
|
|
878
|
+
mediaCardsBlock,
|
|
879
|
+
contactCardBlock,
|
|
880
|
+
locationsBlock,
|
|
881
|
+
videoBlock,
|
|
882
|
+
documentsBlock,
|
|
883
|
+
linkRowBlock,
|
|
884
|
+
documentBlock,
|
|
885
|
+
timetableBlock,
|
|
886
|
+
contactsBlock,
|
|
887
|
+
mediaTextBlock,
|
|
888
|
+
sectionBlock,
|
|
889
|
+
gridBlock,
|
|
890
|
+
stackBlock,
|
|
891
|
+
].map(withLayout) as unknown as [z.AnyZodObject, z.AnyZodObject, ...z.AnyZodObject[]],
|
|
892
|
+
) as unknown as z.ZodType<Block>;
|
|
857
893
|
|
|
858
894
|
/** A page = metadata + an ordered list of blocks. Stored as JSON in the client
|
|
859
895
|
* repo; the AI composes it, a human verifies. This is also the schema of the
|