feat(lexical): add declarative rich text editor

Introduce @workspace/lexical with composable actions, toolbars, embeds, rich-text nodes, media uploads, selection utilities, HTML serialization, and theme styling.\n\nLocalize built-in controls through MessageDescriptor catalogs for English and Simplified Chinese, while providing an English I18nProvider fallback for standalone editor use. Include focused unit coverage for actions, controls, serialization, plugins, public API, and catalogs.
This commit is contained in:
Maofeng
2026-07-31 15:07:09 +08:00
parent 1fe9c1021a
commit 0d817537be
65 changed files with 7453 additions and 0 deletions
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vitest"
import { lexicalMessages } from "../messages"
import { messages as en } from "./en"
import { messages as zhHans } from "./zh-Hans"
describe("Lexical locale catalogs", () => {
it("ships every built-in message in every locale", () => {
const messageIds = Object.values(lexicalMessages)
.map((descriptor) => descriptor.id)
.sort()
for (const messages of [en, zhHans]) {
expect(Object.keys(messages).sort()).toEqual(messageIds)
}
})
})
+9
View File
@@ -0,0 +1,9 @@
import type { lexicalMessages } from "../messages"
export const lexicalCatalogLocales = ["en", "zh-Hans"] as const
type LexicalMessageId =
(typeof lexicalMessages)[keyof typeof lexicalMessages]["id"]
export type LexicalCatalogLocale = (typeof lexicalCatalogLocales)[number]
export type LexicalMessageCatalog = Readonly<Record<LexicalMessageId, string>>
+93
View File
@@ -0,0 +1,93 @@
import type { LexicalMessageCatalog } from "./catalogs"
export const locale = "en"
export const languageTag = "en-US"
export const messages = {
"lexical.action": "Action",
"lexical.actions.alignCenter": "Align center",
"lexical.actions.alignJustify": "Justify",
"lexical.actions.alignLeft": "Align left",
"lexical.actions.alignRight": "Align right",
"lexical.actions.bold": "Bold",
"lexical.actions.bulletList": "Bulleted list",
"lexical.actions.capitalize": "Capitalize",
"lexical.actions.checkList": "Checklist",
"lexical.actions.clearFormatting": "Clear formatting",
"lexical.actions.colorPicker": "Text color",
"lexical.actions.date": "Date",
"lexical.actions.draggableBlocks": "Drag blocks",
"lexical.actions.fontSize": "Font size",
"lexical.actions.heading1": "Heading 1",
"lexical.actions.heading2": "Heading 2",
"lexical.actions.heading3": "Heading 3",
"lexical.actions.horizontalRule": "Horizontal rule",
"lexical.actions.image": "Image",
"lexical.actions.indent": "Increase indent",
"lexical.actions.insertImage": "Insert image",
"lexical.actions.insertLink": "Insert link",
"lexical.actions.insertVideo": "Insert video",
"lexical.actions.italic": "Italic",
"lexical.actions.lowercase": "Lowercase",
"lexical.actions.normal": "Normal text",
"lexical.actions.orderedList": "Numbered list",
"lexical.actions.outdent": "Decrease indent",
"lexical.actions.pasteImage": "Paste images",
"lexical.actions.quote": "Quote",
"lexical.actions.redo": "Redo",
"lexical.actions.strikethrough": "Strikethrough",
"lexical.actions.subscript": "Subscript",
"lexical.actions.superscript": "Superscript",
"lexical.actions.underline": "Underline",
"lexical.actions.undo": "Undo",
"lexical.actions.uppercase": "Uppercase",
"lexical.actions.video": "Video",
"lexical.colorPicker.custom": "Custom color",
"lexical.colorPicker.customValue": "Custom text color value",
"lexical.colorPicker.picker": "Choose a custom text color",
"lexical.colorPicker.preset": "Preset colors",
"lexical.colorPicker.select": "Select text color {color}",
"lexical.common.cancel": "Cancel",
"lexical.common.optional": "Optional",
"lexical.content.placeholder": "Enter content",
"lexical.date.delete": "Delete date",
"lexical.date.edit": "Edit date",
"lexical.date.insert": "Insert date",
"lexical.draggable.dragBlock": "Drag block",
"lexical.groups.insert": "Insert",
"lexical.groups.moreFormatting": "More formatting",
"lexical.groups.paragraphStyle": "Paragraph style",
"lexical.groups.textFormat": "Alignment and indent",
"lexical.link.address": "Link address",
"lexical.link.apply": "Apply link",
"lexical.link.details": "Link details",
"lexical.link.edit": "Edit link",
"lexical.link.remove": "Remove link",
"lexical.media.addCaption": "Add caption",
"lexical.media.alignCenter": "Center image",
"lexical.media.alignLeft": "Align image left",
"lexical.media.alignRight": "Align image right",
"lexical.media.caption": "Caption",
"lexical.media.controls": "Image controls",
"lexical.media.deleteImage": "Delete image",
"lexical.media.editCaption": "Edit caption",
"lexical.media.imageAlt": "Alternative text",
"lexical.media.imageAltDescription": "Leave blank for decorative images; provide it when the image conveys information.",
"lexical.media.imageDescription": "Enter a publicly accessible image URL and provide alternative text.",
"lexical.media.imageUrl": "Image URL",
"lexical.media.uploading": "Uploading image",
"lexical.media.uploadingProgress": "Uploading {progress}%",
"lexical.media.uploadProgress": "Image upload progress",
"lexical.media.urlDescription": "Supports absolute URLs and relative URLs within this site.",
"lexical.media.videoDescription": "Enter a publicly accessible video URL in a browser-supported format.",
"lexical.media.videoPoster": "Video poster URL",
"lexical.media.videoUrl": "Video URL",
"lexical.resize.east": "Resize image to the right",
"lexical.resize.north": "Resize image upward",
"lexical.resize.northEast": "Resize image to the upper right",
"lexical.resize.northWest": "Resize image to the upper left",
"lexical.resize.south": "Resize image downward",
"lexical.resize.southEast": "Resize image to the lower right",
"lexical.resize.southWest": "Resize image to the lower left",
"lexical.resize.west": "Resize image to the left",
} as const satisfies LexicalMessageCatalog
+93
View File
@@ -0,0 +1,93 @@
import type { LexicalMessageCatalog } from "./catalogs"
export const locale = "zh-Hans"
export const languageTag = "zh-CN"
export const messages = {
"lexical.action": "操作",
"lexical.actions.alignCenter": "居中",
"lexical.actions.alignJustify": "两端对齐",
"lexical.actions.alignLeft": "左对齐",
"lexical.actions.alignRight": "右对齐",
"lexical.actions.bold": "粗体",
"lexical.actions.bulletList": "无序列表",
"lexical.actions.capitalize": "首字母大写",
"lexical.actions.checkList": "任务列表",
"lexical.actions.clearFormatting": "清除格式",
"lexical.actions.colorPicker": "文字颜色",
"lexical.actions.date": "日期",
"lexical.actions.draggableBlocks": "内容块拖动",
"lexical.actions.fontSize": "字号",
"lexical.actions.heading1": "标题 1",
"lexical.actions.heading2": "标题 2",
"lexical.actions.heading3": "标题 3",
"lexical.actions.horizontalRule": "分割线",
"lexical.actions.image": "图片",
"lexical.actions.indent": "增加缩进",
"lexical.actions.insertImage": "插入图片",
"lexical.actions.insertLink": "插入链接",
"lexical.actions.insertVideo": "插入视频",
"lexical.actions.italic": "斜体",
"lexical.actions.lowercase": "小写",
"lexical.actions.normal": "正文",
"lexical.actions.orderedList": "有序列表",
"lexical.actions.outdent": "减少缩进",
"lexical.actions.pasteImage": "粘贴图片",
"lexical.actions.quote": "引用",
"lexical.actions.redo": "重做",
"lexical.actions.strikethrough": "删除线",
"lexical.actions.subscript": "下标",
"lexical.actions.superscript": "上标",
"lexical.actions.underline": "下划线",
"lexical.actions.undo": "撤销",
"lexical.actions.uppercase": "大写",
"lexical.actions.video": "视频",
"lexical.colorPicker.custom": "自定义颜色",
"lexical.colorPicker.customValue": "自定义文字颜色值",
"lexical.colorPicker.picker": "使用取色器选择自定义文字颜色",
"lexical.colorPicker.preset": "预设颜色",
"lexical.colorPicker.select": "选择文字颜色 {color}",
"lexical.common.cancel": "取消",
"lexical.common.optional": "可选",
"lexical.content.placeholder": "开始输入…",
"lexical.date.delete": "删除日期",
"lexical.date.edit": "编辑日期",
"lexical.date.insert": "插入日期",
"lexical.draggable.dragBlock": "拖动内容块",
"lexical.groups.insert": "插入",
"lexical.groups.moreFormatting": "更多格式",
"lexical.groups.paragraphStyle": "段落样式",
"lexical.groups.textFormat": "对齐与缩进",
"lexical.link.address": "链接地址",
"lexical.link.apply": "应用链接",
"lexical.link.details": "链接详情",
"lexical.link.edit": "编辑链接",
"lexical.link.remove": "移除链接",
"lexical.media.addCaption": "添加说明",
"lexical.media.alignCenter": "图片居中",
"lexical.media.alignLeft": "图片左对齐",
"lexical.media.alignRight": "图片右对齐",
"lexical.media.caption": "图片说明",
"lexical.media.controls": "图片操作",
"lexical.media.deleteImage": "删除图片",
"lexical.media.editCaption": "编辑图片说明",
"lexical.media.imageAlt": "替代文本",
"lexical.media.imageAltDescription": "纯装饰图片可以留空;有信息含义时请填写。",
"lexical.media.imageDescription": "输入可公开访问的图片地址,并补充替代文本。",
"lexical.media.imageUrl": "图片地址",
"lexical.media.uploading": "正在上传图片",
"lexical.media.uploadingProgress": "正在上传 {progress}%",
"lexical.media.uploadProgress": "图片上传进度",
"lexical.media.urlDescription": "支持绝对地址或站内相对地址。",
"lexical.media.videoDescription": "输入可公开访问的视频地址;支持浏览器可播放的格式。",
"lexical.media.videoPoster": "视频封面地址",
"lexical.media.videoUrl": "视频地址",
"lexical.resize.east": "向右调整图片尺寸",
"lexical.resize.north": "向上调整图片尺寸",
"lexical.resize.northEast": "向右上调整图片尺寸",
"lexical.resize.northWest": "向左上调整图片尺寸",
"lexical.resize.south": "向下调整图片尺寸",
"lexical.resize.southEast": "向右下调整图片尺寸",
"lexical.resize.southWest": "向左下调整图片尺寸",
"lexical.resize.west": "向左调整图片尺寸",
} as const satisfies LexicalMessageCatalog