@comet/agent-features 9.0.0-canary-20260707121405 → 9.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/agent-features",
3
- "version": "9.0.0-canary-20260707121405",
3
+ "version": "9.0.0",
4
4
  "description": "Agent features (skills and rules) for Comet projects",
5
5
  "repository": {
6
6
  "directory": "packages/agent-features",
@@ -294,7 +294,7 @@ All components are imported from `@comet/mail-react` — never from `@faire/mjml
294
294
 
295
295
  ## Blocks
296
296
 
297
- `@comet/mail-react` ships components that render Comet CMS block data — currently `PixelImageBlockData`. Reach for these instead of raw `MjmlImage` / `<img>` whenever the source is a CMS block-data record.
297
+ `@comet/mail-react` ships components that render Comet CMS block data — currently pixel-image and rich-text blocks. Reach for these instead of hand-rolled markup whenever the source is a CMS block-data record.
298
298
 
299
299
  ### Pixel-image blocks
300
300
 
@@ -342,6 +342,94 @@ Without `aspectRatio`, the rendered ratio comes from the DAM crop area. Pass `as
342
342
 
343
343
  When `data.damFile?.image` is absent, both blocks render nothing — no element, no error. Render a placeholder at the call site if you need one.
344
344
 
345
+ ### Rich-text blocks
346
+
347
+ `createRichTextBlock` renders CMS RichText block data (draft-js raw content). **Call the factory once per configuration — at the top level of a file, never inside a component** — and export the returned pair; one configuration drives both rendering contexts.
348
+
349
+ ```tsx title="src/emails/blocks/richText.ts"
350
+ export const { MjmlRichTextBlock, HtmlRichTextBlock } = createRichTextBlock({
351
+ blockTypes: {
352
+ "header-one": { variant: "heading1" },
353
+ "header-two": { variant: "heading2" },
354
+ "paragraph-standard": { variant: "body" },
355
+ },
356
+ });
357
+ ```
358
+
359
+ | Component | Renders each draft block as | Use within |
360
+ | ------------------- | --------------------------- | ---------------------------------------------- |
361
+ | `MjmlRichTextBlock` | `MjmlText` | an `MjmlColumn` (standard MJML layout) |
362
+ | `HtmlRichTextBlock` | `HtmlText` (`<div>`) | raw HTML or MJML ending tags such as `MjmlRaw` |
363
+
364
+ Usage sites pass only `data`:
365
+
366
+ ```tsx
367
+ <MjmlSection indent>
368
+ <MjmlColumn>
369
+ <MjmlRichTextBlock data={richTextData} />
370
+ </MjmlColumn>
371
+ </MjmlSection>
372
+ ```
373
+
374
+ Key behaviors:
375
+
376
+ - **Works without variants.** `createRichTextBlock()` with no options renders every draft block with the base `theme.text` styles — map block types to variants later as the theme grows. Unmapped block types also fall back to base styles.
377
+ - **`blockTypes` values are text-component props**: a theme `variant`, plain (non-responsive) style values (`color`, `fontSize`, `fontWeight`, …), and a `className`. For responsive styling without a variant, set a `className` and register CSS via `registerStyles`:
378
+
379
+ ```tsx
380
+ const { MjmlRichTextBlock } = createRichTextBlock({
381
+ blockTypes: { "header-one": { className: "richTextHeadlineOne" } },
382
+ });
383
+
384
+ registerStyles(
385
+ (theme) => css`
386
+ ${theme.breakpoints.default.belowMediaQuery} {
387
+ .richTextHeadlineOne > div {
388
+ font-size: 24px !important;
389
+ }
390
+ }
391
+ `,
392
+ );
393
+ ```
394
+
395
+ - **Multiple configurations per app.** Each factory call is independent — rename the destructured components per use case:
396
+
397
+ ```tsx
398
+ export const { MjmlRichTextBlock: MjmlHeadlineRichTextBlock, HtmlRichTextBlock: HtmlHeadlineRichTextBlock } = createRichTextBlock({
399
+ blockTypes: { "header-one": { variant: "heading1" }, "header-two": { variant: "heading2" } },
400
+ });
401
+ ```
402
+
403
+ - **Links**: the `external` link type is built in — `LINK` entities with an `external` link block render as `HtmlInlineLink`. Add the application's other link types via `linkTypes`, a resolver per link block type that receives the link block's props and returns the `href` (or `undefined` for no link). Annotate the resolver parameter with the app's generated block-data type so the props are typed without redeclaring their shape. Unconfigured link types render as plain text:
404
+
405
+ ```tsx
406
+ import type { PhoneLinkBlockData } from "@src/blocks.generated";
407
+
408
+ const { MjmlRichTextBlock, HtmlRichTextBlock } = createRichTextBlock({
409
+ linkTypes: {
410
+ phone: (props: PhoneLinkBlockData) => (props.phone ? `tel:${props.phone}` : undefined),
411
+ },
412
+ });
413
+ ```
414
+
415
+ - **Inline styles**: built-in styles (`BOLD`, `ITALIC`, `SUB`, `SUP`, `STRIKETHROUGH`) render out of the box. The `inline` option maps a draft-js inline style name to a renderer and merges over the built-ins — override one, or render a custom style the app adds to its RTE (`customInlineStyles` on `IRteOptions`). The RTE stores only the style name, so the email defines the appearance. Register under the exact style name, use an inline element known to render across email clients (`<span>`, `<strong>`, `<em>` — not `<mark>`), and set explicit styles (email clients apply little of their own):
416
+
417
+ ```tsx
418
+ const { MjmlRichTextBlock, HtmlRichTextBlock } = createRichTextBlock({
419
+ inline: {
420
+ HIGHLIGHT: (children, { key }) => (
421
+ <span key={key} style={{ backgroundColor: "#ff0000", color: "#ffffff" }}>
422
+ {children}
423
+ </span>
424
+ ),
425
+ },
426
+ });
427
+ ```
428
+
429
+ - **Lists** render flat (`<ul>` / `<ol>` inside one text component per list); nesting by draft depth isn't supported.
430
+ - Spacing between blocks comes from the theme's `bottomSpacing` (the last block gets none); headings are styled text, not semantic `<h1>` elements.
431
+ - Rendered elements carry `richTextBlock__text`, `richTextBlock__list`, `richTextBlock__listItem`, and `richTextBlock__link` class names for targeting with `registerStyles`.
432
+
345
433
  ---
346
434
 
347
435
  ## Custom Components