From 7f83c117d9f2cba9c8c11ebc63d8baa71cf40cc0 Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Fri, 15 May 2026 16:43:24 +0100 Subject: [PATCH] vault backup: 2026-05-15 16:43:24 --- .../rich-text__converting-markdown.md | 0 .../rich-text__converting-plaintext.md | 0 wiki/_master-index.md | 2 +- wiki/payloadcms/_index.md | 1 + .../rich-text-converting-plaintext.md | 84 ++++++ wiki/payloadcms/rich-text-custom-features.md | 265 ++++++++++++++++++ 6 files changed, 351 insertions(+), 1 deletion(-) rename raw/{ => _processed}/rich-text__converting-markdown.md (100%) rename raw/{ => _processed}/rich-text__converting-plaintext.md (100%) create mode 100644 wiki/payloadcms/rich-text-converting-plaintext.md create mode 100644 wiki/payloadcms/rich-text-custom-features.md diff --git a/raw/rich-text__converting-markdown.md b/raw/_processed/rich-text__converting-markdown.md similarity index 100% rename from raw/rich-text__converting-markdown.md rename to raw/_processed/rich-text__converting-markdown.md diff --git a/raw/rich-text__converting-plaintext.md b/raw/_processed/rich-text__converting-plaintext.md similarity index 100% rename from raw/rich-text__converting-plaintext.md rename to raw/_processed/rich-text__converting-plaintext.md diff --git a/wiki/_master-index.md b/wiki/_master-index.md index 7d7be6b..6cc2285 100644 --- a/wiki/_master-index.md +++ b/wiki/_master-index.md @@ -35,7 +35,7 @@ This 3-hop pattern works for hundreds of articles without vector search. | [[wiki/reports/_index\|reports/]] | Weekly and monthly summaries — generate: `uv run python scripts/report-generator.py --weekly` | 1 | | [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 12 | | [[wiki/testing/_index\|testing/]] | Web app testing: functional, performance, security, UI types; TDD/BDD/Agile methodologies; Selenium/Cypress/Playwright/JMeter/OWASP ZAP tools | 1 | -| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 135 | +| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 136 | | [[wiki/shared-patterns/_index\|shared-patterns/]] | Oliver Agency standard library patterns: httpx, structlog, pydantic-settings, alembic — reuse before writing from scratch | 4 | | [[wiki/mistakes/_index\|mistakes/]] | Anti-patterns extracted from sessions — per-stack running lists (fastapi, react, docker, postgres, general) — injected at session start | 5 | diff --git a/wiki/payloadcms/_index.md b/wiki/payloadcms/_index.md index 60fdc82..cc183ee 100644 --- a/wiki/payloadcms/_index.md +++ b/wiki/payloadcms/_index.md @@ -136,3 +136,4 @@ | [[wiki/payloadcms/rich-text-converting-html\|Rich Text — Converting HTML]] | `convertLexicalToHTML` (sync/async), `convertHTMLToLexical`, image data-attributes pattern, `buildEditorState` helper, block converters | raw/rich-text__converting-html.md | 2026-05-15 | | [[wiki/payloadcms/rich-text-converting-jsx\|Rich Text — Converting to JSX]] | `RichText` component, custom converters function, internal link `internalDocToHref`, custom block converters per slug, overriding built-ins (e.g. upload → next/image) | raw/rich-text__converting-jsx.md | 2026-05-15 | | [[wiki/payloadcms/rich-text-converting-markdown\|Rich Text — Converting Markdown]] | `convertLexicalToMarkdown` / `convertMarkdownToLexical`, editorConfigFactory, sibling textarea hook pattern, Upload `![media:]()` placeholder, MDX block `jsx.export`/`jsx.import` | raw/rich-text__converting-markdown.md | 2026-05-15 | +| [[wiki/payloadcms/rich-text-converting-plaintext\|Rich Text — Converting to Plaintext]] | `convertLexicalToPlaintext`, no built-in defaults, fallback heuristics (text→children→ignore), paragraph/text/tab newlines, custom `PlaintextConverters` | raw/rich-text__converting-plaintext.md | 2026-05-15 | diff --git a/wiki/payloadcms/rich-text-converting-plaintext.md b/wiki/payloadcms/rich-text-converting-plaintext.md new file mode 100644 index 0000000..abc0e03 --- /dev/null +++ b/wiki/payloadcms/rich-text-converting-plaintext.md @@ -0,0 +1,84 @@ +--- +title: "Rich Text — Converting to Plaintext" +aliases: [lexical-to-plaintext, convert-richtext-plaintext] +tags: [payloadcms, lexical, rich-text, plaintext, converters] +sources: [raw/rich-text__converting-plaintext.md] +created: 2026-05-15 +updated: 2026-05-15 +--- + +## Overview + +`@payloadcms/richtext-lexical/plaintext` provides `convertLexicalToPlaintext` to strip Lexical JSON down to a plain string — useful for search indexing, excerpts, or email previews. + +## Basic Usage + +```ts +import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' +import { convertLexicalToPlaintext } from '@payloadcms/richtext-lexical/plaintext' + +const data: SerializedEditorState = { /* your richtext data */ } + +const plaintext = convertLexicalToPlaintext({ data }) +``` + +## Custom Converters + +Pass a `converters` object to override how specific node types are rendered. + +```ts +import type { DefaultNodeTypes, SerializedBlockNode } from '@payloadcms/richtext-lexical' +import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical' +import type { MyTextBlock } from '@/payload-types' +import { + convertLexicalToPlaintext, + type PlaintextConverters, +} from '@payloadcms/richtext-lexical/plaintext' + +const converters: PlaintextConverters< + DefaultNodeTypes | SerializedBlockNode +> = { + blocks: { + textBlock: ({ node }) => node.fields.text ?? '', + }, + link: ({ node }) => node.fields.url ?? '', +} + +const plaintext = convertLexicalToPlaintext({ converters, data }) +``` + +## Fallback Heuristics + +Unlike HTML/JSX/Markdown converters, **there are no built-in default converters** for plaintext. For nodes without a defined converter, the following heuristics apply in order: + +| Node has… | Behavior | +|-----------|----------| +| `text` field | Use it as plaintext | +| `children` field | Recursively convert children | +| Neither | Ignored (node produces no output) | + +Special nodes: +- **Paragraph** → inserts a newline (`\n`) +- **Text** → inserts the text value +- **Tab** → inserts a tab character (`\t`) + +## Key Takeaways + +- Import from `@payloadcms/richtext-lexical/plaintext` (separate entrypoint). +- No default converters — you must handle custom block/node types yourself or rely on the fallback heuristics. +- Fallback order: `text` field → `children` (recursive) → ignored. +- Paragraph/text/tab nodes emit `\n` / text / `\t` automatically. +- Use `PlaintextConverters>` for TypeScript-safe custom converters. +- Common use cases: full-text search indexing, email body generation, excerpt/preview generation. + +## Related + +- [[wiki/payloadcms/rich-text-converters|Rich Text — Converters Overview]] — HTML, JSX, Markdown, Plaintext converter architecture +- [[wiki/payloadcms/rich-text-converting-html|Rich Text — Converting HTML]] — `convertLexicalToHTML` and `convertHTMLToLexical` +- [[wiki/payloadcms/rich-text-converting-jsx|Rich Text — Converting to JSX]] — `RichText` component and custom JSX converters +- [[wiki/payloadcms/rich-text-converting-markdown|Rich Text — Converting Markdown]] — Markdown ↔ Lexical round-trip + +## Sources + +- `raw/rich-text__converting-plaintext.md` +- https://payloadcms.com/docs/rich-text/converting-plaintext diff --git a/wiki/payloadcms/rich-text-custom-features.md b/wiki/payloadcms/rich-text-custom-features.md new file mode 100644 index 0000000..f58fbf9 --- /dev/null +++ b/wiki/payloadcms/rich-text-custom-features.md @@ -0,0 +1,265 @@ +--- +title: "Rich Text — Custom Lexical Features" +aliases: [lexical-custom-features, custom-lexical-feature, payload-lexical-feature] +tags: [payloadcms, lexical, rich-text, custom-feature, nodes, toolbar, slash-menu] +sources: [raw/rich-text__custom-features.md] +created: 2026-05-15 +updated: 2026-05-15 +--- + +## Overview + +Custom Lexical features are modular extensions to the Payload rich text editor. Each feature is split into exactly two files: + +- `feature.server.ts` — server-side entry point (nodes, markdown transformers, i18n, hooks) +- `feature.client.ts` — client-side UI (toolbar, slash menu, plugins, React node components) + +The server feature is the sole entry point; it registers the client feature via an import path. + +> **IMPORTANT:** Never import directly from `lexical`, `@lexical/*`, or `@payloadcms/richtext-lexical` on the client side. Use the re-exported paths instead: +> - Server/shared: `@payloadcms/richtext-lexical/lexical/utils` +> - Client: `@payloadcms/richtext-lexical/client` + +## Do You Need a Custom Feature? + +Before building a custom feature, consider [[wiki/payloadcms/rich-text-blocks|BlocksFeature]] — it supports: +- **Block blocks** — full-line custom blocks with arbitrary React components +- **Inline blocks** — insertable within paragraphs + +Only build a custom feature if BlocksFeature cannot fulfill the requirement (e.g. custom nodes with lexical commands, toolbar groups, complex markdown transforms). + +## Server Feature + +### Minimal scaffold + +```ts +import { createServerFeature } from '@payloadcms/richtext-lexical' + +export const MyFeature = createServerFeature({ + feature: {}, + key: 'myFeature', +}) +``` + +Register in editor config: + +```ts +editor: lexicalEditor({ features: [MyFeature()] }) +``` + +### Server Feature properties + +| Property | Description | +|----------|-------------| +| `i18n` | Translations scoped to the feature key (`lexical::