Blocs de page : 15 sections accessibles (hero, tarifs, FAQ avec JSON-LD…), éditeur d'admin et registre extensible
Installer
genpm add @core/blocksCe que vous obtenez
- Source dans src/lib/blocks/, 25 fichiers. (52,7 ko)
- Règles IA dans src/lib/blocks/AGENTS.md, plus les fichiers de règles de l’IDE.
- Résout @core/admin, @core/contracts, @core/db, @core/media, @core/rich-text, @core/ui pour vous.
README
Ce paquet n’a pas de README.
Voici exactement ce que lit votre IA quand elle travaille dans src/lib/blocks. Rien d’autre n’est ajouté à son contexte.
@core/blocks — rules for AI agents
Purpose
Page sections that editors combine from the admin: hero, features, benefits, testimonials, logos, pricing, FAQ (with FAQPage JSON-LD), call to action, gallery, video, stats, comparison, guarantee, rich text and form. Each block is a zod schema + editor fields + a React component. Other packages and kits add their own blocks (product grid, latest posts…) to the same registry. Blocks are stored as JSON inside @core/content entries; no tables of its own.
Map
index.ts—defineBlock,createBlockRegistry,coreBlocks(and each block),RenderBlocks,collectMediaIds.core/*.ts— one file per block.parts.ts—Section,Heading,Img,CtaLink,JsonLdfor new blocks.media.ts—loadBlockMedia(blocks, registry)(server: one query to @core/media).editor.ts—'use client':blocksRenderer(blockMeta(registry))forAdminApp.blocks.css— styles on @core/ui tokens.
Integration
- Create
src/genpm/blocks.ts:export const blocks = createBlockRegistry([...coreBlocks /*, kit blocks */]); - Add a field to a content collection: schema
blocks: blocks.schema, admin fieldblocks.field('blocks', 'Sections'). - Admin: compute
blockMeta(blocks)on the server and pass it to the client component that renders<AdminApp renderers={{ blocks: blocksRenderer(meta), /* richText, media… */ }} />(never import the registry in client code). - Page (server component):
const media = await loadBlockMedia(page.data.blocks, blocks); return <RenderBlocks blocks={page.data.blocks} registry={blocks} media={media} renderForm={(key) => <Form formKey={key} />} embeds={hasConsent('marketing') ? 'iframe' : 'link'} />; - Styles: import
src/lib/ui/ui.cssthensrc/lib/blocks/blocks.cssin the root layout. - Verify: a page with every block passes axe and has one
h1; a block with an unknown type renders nothing and warns in dev.
Conventions
- A new block = new file with
defineBlock+ entry in the registry. Never addif (type === …)toRenderBlocks. - Use
SectionandHeadingwithctx.level(items one level below); only blocks withpageTitlemay become theh1. - Images are media ids resolved with
loadBlockMedia; links go through thehrefschema (nojavascript:). - Text in blocks is plain text; use the
rich-textblock for formatting.
Don't
- Don't render user HTML (
dangerouslySetInnerHTML) except viaJsonLd, which escapes<,>and&. - Don't load video iframes without marketing consent (
embeds: 'link'is the default). - Don't invent testimonials, ratings or statistics; the editor help texts remind users these must be real and verifiable.
# @core/blocks — rules for AI agents
## Purpose
Page sections that editors combine from the admin: hero, features, benefits, testimonials, logos, pricing, FAQ (with
FAQPage JSON-LD), call to action, gallery, video, stats, comparison, guarantee, rich text and form. Each block is a
zod schema + editor fields + a React component. Other packages and kits add their own blocks (product grid, latest
posts…) to the same registry. Blocks are stored as JSON inside @core/content entries; no tables of its own.
## Map
- `index.ts` — `defineBlock`, `createBlockRegistry`, `coreBlocks` (and each block), `RenderBlocks`, `collectMediaIds`.
- `core/*.ts` — one file per block. `parts.ts` — `Section`, `Heading`, `Img`, `CtaLink`, `JsonLd` for new blocks.
- `media.ts` — `loadBlockMedia(blocks, registry)` (server: one query to @core/media).
- `editor.ts` — `'use client'`: `blocksRenderer(blockMeta(registry))` for `AdminApp`. `blocks.css` — styles on @core/ui tokens.
## Integration
1. Create `src/genpm/blocks.ts`: `export const blocks = createBlockRegistry([...coreBlocks /*, kit blocks */]);`
2. Add a field to a content collection: schema `blocks: blocks.schema`, admin field `blocks.field('blocks', 'Sections')`.
3. Admin: compute `blockMeta(blocks)` on the server and pass it to the client component that renders
`<AdminApp renderers={{ blocks: blocksRenderer(meta), /* richText, media… */ }} />` (never import the registry in client code).
4. Page (server component):
```tsx
const media = await loadBlockMedia(page.data.blocks, blocks);
return <RenderBlocks blocks={page.data.blocks} registry={blocks} media={media}
renderForm={(key) => <Form formKey={key} />} embeds={hasConsent('marketing') ? 'iframe' : 'link'} />;
```
5. Styles: import `src/lib/ui/ui.css` then `src/lib/blocks/blocks.css` in the root layout.
6. Verify: a page with every block passes axe and has one `h1`; a block with an unknown type renders nothing and warns in dev.
## Conventions
- A new block = new file with `defineBlock` + entry in the registry. Never add `if (type === …)` to `RenderBlocks`.
- Use `Section` and `Heading` with `ctx.level` (items one level below); only blocks with `pageTitle` may become the `h1`.
- Images are media ids resolved with `loadBlockMedia`; links go through the `href` schema (no `javascript:`).
- Text in blocks is plain text; use the `rich-text` block for formatting.
## Don't
- Don't render user HTML (`dangerouslySetInnerHTML`) except via `JsonLd`, which escapes `<`, `>` and `&`.
- Don't load video iframes without marketing consent (`embeds: 'link'` is the default).
- Don't invent testimonials, ratings or statistics; the editor help texts remind users these must be real and verifiable.
L’arborescence exacte qui sera injectée, après .genpmignore. Épinglée à
import { createElement as h, type ReactNode } from 'react';
import { z } from 'zod';
import { defineBlock } from '../define.ts';
import { optText, Section, text } from '../parts.ts';
const cell = (v: string): ReactNode => {
const t = v.trim().toLowerCase();
if (t === 'yes' || t === 'true') return h('span', null, h('span', { 'aria-hidden': true }, '✓'), h('span', { className: 'ui-visually-hidden' }, 'Yes'));
if (t === 'no' || t === 'false') return h('span', null, h('span', { 'aria-hidden': true }, '✗'), h('span', { className: 'ui-visually-hidden' }, 'No'));
return v;
};
export const comparison = defineBlock({
type: 'comparison',
label: 'Comparison',
icon: 'table',
schema: z.object({
title: optText(160),
us: text(60),
them: text(60),
rows: z.array(z.object({ feature: text(160), us: text(80), them: text(80) })).min(1).max(30),
}),
fields: [
{ name: 'title', label: 'Title', type: 'text' },
{ name: 'us', label: 'Our column', type: 'text', required: true },
{ name: 'them', label: 'Other column', type: 'text', required: true, help: 'Comparative claims must be objective and verifiable.' },
{
name: 'rows',
label: 'Rows ("yes"/"no" show as ✓/✗)',
type: 'list',
required: true,
fields: [
{ name: 'feature', label: 'Feature', type: 'text', required: true },
{ name: 'us', label: 'Ours', type: 'text', required: true },
{ name: 'them', label: 'Theirs', type: 'text', required: true },
],
},
],
defaults: () => ({ us: 'Us', them: 'Others', rows: [{ feature: 'Feature', us: 'yes', them: 'no' }] }),
Component: ({ data, ctx }) =>
h(
Section,
{ id: ctx.id, block: 'comparison', title: data.title, ctx, label: 'Comparison' },
h(
'table',
{ className: 'ui-table' },
h('caption', { className: 'ui-visually-hidden' }, data.title ?? `${data.us} / ${data.them}`),
h('thead', null, h('tr', null, h('td', null), h('th', { scope: 'col' }, data.us), h('th', { scope: 'col' }, data.them))),
h('tbody', null, data.rows.map((r, i) => h('tr', { key: i }, h('th', { scope: 'row' }, r.feature), h('td', null, cell(r.us)), h('td', null, cell(r.them))))),
),
),
});
Ce paquet ne déclare aucun serveur MCP.
| Version | Commit | Publié | Analyse |
|---|---|---|---|
| 1.0.0 | a255d6d | il y a 4 heures | analyse réussie |
- genpm
- @core/admin ^1.0.0@core/contracts ^1.0.0@core/db ^1.0.0@core/media ^1.0.0@core/rich-text ^1.0.0@core/ui ^1.0.0
- proposé
- GenPM propose la commande npm et ne l’exécute que si vous acceptez.
- Utilisé par (1)
- @core/kit-cms ^1.0.0
- analyse
- analyse réussie · 0 problème
- commit
- v1.0.0 → a255d6d08201d6342d07ad41d96123ea1407a5f1 · vérifié après téléchargement
- scripts
- Aucun. GenPM n’exécute jamais le code des paquets.
- licence
- MIT
- Qualité
- 100/100
- Licence reconnuevalidé
- AGENTS.md explique son objectifvalidé
- AGENTS.md donne les étapes d’intégrationvalidé
- AGENTS.md liste conventions ou interditsvalidé
- Contient des testsvalidé
- Analyse de sécurité réussievalidé
- Publié au cours des 6 derniers moisvalidé
- Éditeur vérifiévalidé
- Résumé et mots-clésvalidé
- signalement
- Vous avez repéré un problème ?