@standhigher/react-email-editor 0.4.1 → 0.5.1

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/dist/styles.css CHANGED
@@ -693,11 +693,6 @@
693
693
  background: var(--ree-panel-secondary);
694
694
  }
695
695
 
696
- .ree-personalization-menu {
697
- left: 0;
698
- min-width: 240px;
699
- }
700
-
701
696
  .ree-subject-hint {
702
697
  flex: 0 1 auto;
703
698
  margin: 0;
@@ -1670,16 +1665,30 @@
1670
1665
 
1671
1666
  /* ---------- 变量 ---------- */
1672
1667
 
1673
- .ree-var-wrap {
1668
+ .ree-input-with-variable {
1674
1669
  position: relative;
1675
1670
  }
1676
1671
 
1677
- .ree-var-btn {
1672
+ .ree-input-with-variable > .ree-input {
1673
+ --ree-control-padding-end: 48px;
1674
+ }
1675
+
1676
+ .ree-input-with-variable > .ree-textarea {
1677
+ display: block;
1678
+ }
1679
+
1680
+ .ree-personalization-suffix {
1681
+ position: absolute;
1682
+ top: 6px;
1683
+ right: 6px;
1684
+ }
1685
+
1686
+ .ree-personalization-suffix-btn {
1678
1687
  appearance: none;
1679
1688
  border: none;
1680
1689
  background: transparent;
1681
1690
  min-width: 30px;
1682
- height: 24px;
1691
+ height: 26px;
1683
1692
  padding: 0 6px;
1684
1693
  border-radius: var(--ree-radius-sm);
1685
1694
  cursor: pointer;
@@ -1687,10 +1696,16 @@
1687
1696
  font-size: 12px;
1688
1697
  }
1689
1698
 
1690
- .ree-var-btn:hover {
1699
+ .ree-personalization-suffix-btn:hover {
1691
1700
  background: var(--ree-panel-secondary);
1692
1701
  }
1693
1702
 
1703
+ .ree-personalization-suffix-btn:focus-visible,
1704
+ .ree-personalization-btn:focus-visible {
1705
+ outline: 2px solid var(--ree-text);
1706
+ outline-offset: -2px;
1707
+ }
1708
+
1694
1709
  .ree-var-menu {
1695
1710
  position: absolute;
1696
1711
  top: 100%;
@@ -1707,6 +1722,16 @@
1707
1722
  color: var(--ree-text, #202223);
1708
1723
  }
1709
1724
 
1725
+ .ree-var-menu.ree-personalization-menu {
1726
+ position: fixed;
1727
+ inset: auto;
1728
+ margin: 0;
1729
+ z-index: 1000;
1730
+ width: 240px;
1731
+ min-width: 0;
1732
+ max-width: calc(100vw - 16px);
1733
+ }
1734
+
1710
1735
  .ree-var-item {
1711
1736
  display: flex;
1712
1737
  flex-direction: column;
@@ -1730,6 +1755,13 @@
1730
1755
  color: var(--ree-primary);
1731
1756
  }
1732
1757
 
1758
+ .ree-var-name,
1759
+ .ree-var-label {
1760
+ max-width: 100%;
1761
+ overflow-wrap: anywhere;
1762
+ white-space: pre-wrap;
1763
+ }
1764
+
1733
1765
  .ree-var-label {
1734
1766
  font-size: 12px;
1735
1767
  color: var(--ree-muted);
@@ -1862,28 +1894,6 @@
1862
1894
  margin-bottom: 14px;
1863
1895
  }
1864
1896
 
1865
- .ree-field-label:has(> .ree-var-wrap) {
1866
- position: relative;
1867
- justify-content: flex-start;
1868
- }
1869
-
1870
- .ree-field-label > .ree-var-wrap {
1871
- position: static;
1872
- }
1873
-
1874
- .ree-field-label .ree-var-menu {
1875
- left: 0;
1876
- width: min(280px, 100%);
1877
- min-width: 0;
1878
- }
1879
-
1880
- .ree-field-label .ree-var-name,
1881
- .ree-field-label .ree-var-label {
1882
- max-width: 100%;
1883
- overflow-wrap: anywhere;
1884
- white-space: pre-wrap;
1885
- }
1886
-
1887
1897
  /* ---------- 配置控件 ---------- */
1888
1898
 
1889
1899
  .ree-textarea {
@@ -262,4 +262,8 @@ export const fragment = blockToHtml(block, undefined, "email", {
262
262
  });
263
263
  ```
264
264
 
265
- Complete documents shallow-merge variables → templateData → block.defaultData, with later data winning. Single-block rendering does not merge variables automatically; this example merges explicitly and uses the singular resolveHtmlTemplate option. Your application chooses the actual sending engine; preview only supports a subset of Go templates.
265
+ Complete documents shallow-merge variables → templateData → block.defaultData, with later data winning. Preview and sending apply listed-variable dot normalization while preserving the stored template source. Single-block rendering does not merge variables automatically; this example merges explicitly and uses the singular resolveHtmlTemplate option. Your application chooses the actual sending engine. The default preview supports basic syntax; inject templateEngine for complete Go semantics.
266
+
267
+ ## Custom template engine and standalone preview
268
+
269
+ See [Template preview and custom engines](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.md) for supported syntax, adapter behavior and integration examples.
@@ -262,4 +262,8 @@ export const fragment = blockToHtml(block, undefined, "email", {
262
262
  });
263
263
  ```
264
264
 
265
- 完整文档自动按顶层浅合并 `variables → templateData → block.defaultData`,后者优先。单区块不会自动合并变量;示例显式合并,并使用单数 `resolveHtmlTemplate`。真实发送端的模板引擎由应用决定,预览仅支持部分 Go template 语法。
265
+ 完整文档自动按顶层浅合并 `variables → templateData → block.defaultData`,后者优先。预览和发送会先按变量清单补点,存储中的模板原文保持不变;单区块不会自动合并变量,示例显式合并,并使用单数 `resolveHtmlTemplate`。真实发送端的模板引擎由应用决定;默认预览仅支持基础语法,完整 Go 语义通过 `templateEngine` 注入。
266
+
267
+ ## 自定义模板引擎与独立预览
268
+
269
+ 完整说明与接入示例见 [模板预览与自定义引擎](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.zh-CN.md)。
package/docs/api.md CHANGED
@@ -68,6 +68,7 @@ Requires an instance from `useEmailEditor()`. Returns cached `{ document }`, a d
68
68
  | `plugins` | `BlockPlugin[]` | `[]`; plugins created by `defineBlock()`. |
69
69
  | `builtins` | `false \| string[]` | All by default; false hides all builtins; arrays filter and order them. Hidden blocks still render existing nodes. Custom types are appended. |
70
70
  | `pickImage` | `(context: ImagePickerContext) => Promise<string \| null>` | Resolve a URL; null cancels; throwing shows retry. Context contains `value/label/signal`. |
71
+ | `templateEngine` | `TemplateEngine` | Shared synchronous template adapter; defaults to the basic engine. |
71
72
 
72
73
  Declare plugins at module scope, or use `useMemo` when they depend on application state. Stable option references reuse configuration; inline arrays do not recreate the editor or reset its document. The returned object contains:
73
74
 
@@ -76,6 +77,7 @@ Declare plugins at module scope, or use `useMemo` when they depend on applicatio
76
77
  | `definitions` | Readonly list of all registered plugins, including hidden builtins. |
77
78
  | `library` | Readonly list of insertable blocks in display order. |
78
79
  | `pickImage` | Registered resource picker. |
80
+ | `templateEngine` | Normalized engine shared by canvas, preview and resolved export. |
79
81
  | `createBlock(type)` | Registered type string → detached default `EmailBlockNode`; unknown types throw. |
80
82
  | `toHtml(document, options?)` | v4 document and `DocumentHtmlOptions` → complete HTML using this registry. |
81
83
  | `validate(document)` | Document → `ValidationIssue[]`, without rendering HTML. |
@@ -100,7 +102,7 @@ export const coupon = defineBlock({
100
102
  | `type` / `title` | Required nonempty unique type and library title. |
101
103
  | `renderEmail(ctx)` | Required; returns inner HTML or `EmailMarkup`; ctx contains `block/controls/style/body/mode/email`. |
102
104
  | `controls` | Map of names to EmailControls declarations, default `{}`; values and defaults are inferred. |
103
- | `icon` / `description` | Optional ReactNode/string icon and description. |
105
+ | `icon` / `description` | Optional ReactNode/HTML string icon and description. The icon defaults to the first character of the trimmed `title`, preserving case and displaying it as text. |
104
106
  | `override` | Must be true to replace a builtin with the same type. |
105
107
  | `containerStyles` | Spacing and appearance enabled by default; false disables all; `{ spacing?: boolean, appearance?: boolean }` controls each group. |
106
108
  | `styleControls` | Additional styles; matching IDs replace default container controls. |
@@ -166,7 +168,9 @@ Use these components only within a plugin Canvas or Inspector.
166
168
  | `getBlockDefinition(type, definitions?)` | Type and optional registry, default builtins | Matching runtime definition or undefined. Do not mutate frozen configuration definitions. |
167
169
  | `generateId(prefix = "block")` | String ID prefix | String with a random suffix; applications must still ensure IDs are unique within a document. |
168
170
 
169
- EmailDocument contains version/body/blocks/subject?; EmailBlockNode contains id/type/controls/style?/defaultData?. Only DOCUMENT_VERSION = 4 is supported; older documents are not migrated. JSON stores no components or render functions; register matching plugins again when loading it.
171
+ EmailDocument contains version/body/blocks/subject?; EmailBlockNode contains id/type/controls/style?/defaultData?. `version: number` is only a format identifier: new documents use `DOCUMENT_VERSION = 4`, while loading and exporting preserve the supplied value without version-based rejection or automatic migration of older structures. JSON stores no components or render functions; register matching plugins again when loading it.
172
+
173
+ Initialization, `setDocument()`, `config.validate()` and HTML export require a document object, a `blocks` array, non-empty string `id/type` values and an object `controls` for every block. Invalid structure throws a `TypeError` with the field path. A supplied `subject` must be a string; a supplied non-null `body` must be an object, with omitted settings still filled by `resolveEmailBody()`. Nodes with only legacy `fields` and no `controls` are rejected; control values continue to be normalized and validated by the plugin schema.
170
174
 
171
175
  Canvas, preview and email HTML share body settings:
172
176
 
@@ -175,7 +179,7 @@ Canvas, preview and email HTML share body settings:
175
179
  | textColor | #000000; default text color. |
176
180
  | backgroundColor | #ffffff; centered content background; outside remains white. |
177
181
  | contentWidth | 600; maximum content width in px. |
178
- | fontFamily | Arial, Helvetica, sans-serif. |
182
+ | fontFamily | `""`; the inspector shows `Default`. Omitted or empty values render with Arial, Helvetica, sans-serif. |
179
183
  | fontWeight | normal; also supports bold. |
180
184
  | linkColor / linkDecoration | #005bd3 / underline. |
181
185
  | contentPadding | 0px; CSS padding inside the border. |
@@ -195,6 +199,7 @@ The required document must use v4. Definitions default to DEFAULT_BLOCKS; for cu
195
199
  | resolveHtmlTemplates | false preserves templates; true resolves preview data. |
196
200
  | variables | [] of exact inserted tokens and preview values. |
197
201
  | templateData | Optional TemplateData for global objects and arrays. |
202
+ | templateEngine | Optional TemplateEngine; defaults to the basic engine. |
198
203
 
199
204
  Data is shallow-merged at the top level: node defaultData > templateData > variables. Default export only adds missing dots to tokens exactly matching variables[].name, preserving spaces: `{{ store_name }}` → `{{ .store_name }}`. Unlisted Go templates stay unchanged. Empty variable descriptions are hidden; value is only preview data.
200
205
 
@@ -208,12 +213,19 @@ Data is shallow-merged at the top level: node defaultData > templateData > varia
208
213
  | `buildRenderContext(node, mode, body = DEFAULT_EMAIL_BODY, definition = builtin lookup)` | Required mode; explicitly pass a custom definition | Detached normalized BlockRenderContext without rendering. |
209
214
  | `variablesToTemplateData(variables)` | Required Variable[] | TemplateData keyed by token paths with value as preview data; ignores absent values. |
210
215
  | `mergeTemplateData(variables, defaultData?)` | Variables and optional object | Shallow merge, defaultData wins; undefined if both contain no data. |
211
- | `renderTemplatePreview(html, defaultData?)` | HTML string and optional data | Supports a subset of Go paths, range, if and with; preserves input when data is omitted. |
216
+ | `renderTemplatePreview(template, data?, options?)` | Template, data and TemplateEngineOptions | Synchronous preview with the selected engine. The basic engine preserves the entire input when it contains unsupported syntax. |
217
+ | `validateTemplate(template, options?)` | Template and TemplateEngineOptions | TemplateValidationIssue[]; undefined when validation is unavailable. Never invoked automatically. |
218
+
219
+ Both DocumentHtmlOptions and BlockRenderOptions accept templateEngine?: TemplateEngine. BlockRenderOptions contains resolveHtmlTemplate?: boolean (singular), templateData?: TemplateData, variables?: Variable[], body?: EmailBodySettings. Body defaults to DEFAULT_EMAIL_BODY; templates are preserved by default. Single-block preview uses templateData ?? node.defaultData and does not merge variables automatically; call mergeTemplateData explicitly when needed. The fourth argument must be an object; boolean or null throws TypeError.
212
220
 
213
- BlockRenderOptions contains resolveHtmlTemplate?: boolean (singular), templateData?: TemplateData, variables?: Variable[], body?: EmailBodySettings. Body defaults to DEFAULT_EMAIL_BODY; templates are preserved by default. Single-block preview uses templateData ?? node.defaultData and does not merge variables automatically; call mergeTemplateData explicitly when needed. The fourth argument must be an object; boolean or null throws TypeError.
221
+ config.toHtml() uses the configured engine unless options.templateEngine explicitly overrides it. Passing config.definitions alone does not carry engine configuration.
214
222
 
215
223
  Mode selects the consumer: canvas for editing, email for control validation. It does not enable template resolution. Preview is a subset of Go templates, not a complete Go engine, HTML sanitizer or security sandbox.
216
224
 
225
+ ### Custom template engines
226
+
227
+ See [Template preview and custom engines](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.md) for supported syntax, adapter behavior and integration examples.
228
+
217
229
  ### email helpers in renderEmail
218
230
 
219
231
  | Method | Arguments and output |
@@ -250,7 +262,7 @@ Props and `RichText.inherited` fields are documented in the published `.d.ts`; u
250
262
  | --- | --- |
251
263
  | BLOCK_TYPES / BuiltinBlockType | heading/text/button/image/divider/spacer/html. |
252
264
  | DEFAULT_BLOCKS | All builtin BlockPlugin definitions; custom registries use config.definitions. |
253
- | DEFAULT_EMAIL_BODY / DOCUMENT_VERSION | Default body / document version 4. Do not mutate the default object. |
265
+ | DEFAULT_EMAIL_BODY / DOCUMENT_VERSION | Default body / format identifier 4 for new documents. Do not mutate the default object. |
254
266
  | FONT_FAMILIES | Readonly label/value font options. |
255
267
  | STYLE_GROUPS / StyleGroup | general/dimension/typography/decorations identifiers and display names. |
256
268
  | BORDER_STYLES / BorderStyle | none/solid/dashed/dotted. |
@@ -264,12 +276,14 @@ Fields in editor, document, control, plugin, style, rendering and template types
264
276
  ## Development and local linking
265
277
 
266
278
  ```bash
267
- pnpm typecheck
268
- pnpm lint
269
- pnpm build
279
+ npm run typecheck
280
+ npm run lint
281
+ npm run build
270
282
  node --test tests/*.test.mjs tests/*.test.ts
271
283
  ```
272
284
 
285
+ Use `npm run check` to run all four checks in order, or `npm test` after building to run only the Node tests. Node 24 LTS and npm 11.13.0 are the repository defaults. See the [release guide](./release.md) for package verification and publishing.
286
+
273
287
  Build before tests that depend on dist. Internal TypeScript tests require Node 22.18+. The package ships ESM, declarations and flattened CSS. React/React DOM are peer dependencies; TipTap, dnd-kit, CodeMirror and Lezer are dependencies, externalized during builds rather than bundled.
274
288
 
275
- Local consumers can use link:../react-email-editor. Run pnpm build first and pnpm build:watch to update dist. Both projects must share one React instance: Vite can use resolve.dedupe: ["react", "react-dom"]; Next.js can use transpilePackages: ["@standhigher/react-email-editor"], with Turbopack root covering the linked directory.
289
+ Local consumers can run `npm install ../react-email-editor`. Run `npm run build` first and `npm run build:watch` to update dist. Both projects must share one React instance: Vite can use resolve.dedupe: ["react", "react-dom"]; Next.js can use transpilePackages: ["@standhigher/react-email-editor"], with Turbopack root covering the linked directory.
package/docs/api.zh-CN.md CHANGED
@@ -68,6 +68,7 @@ export function EmailPage() {
68
68
  | `plugins` | `BlockPlugin[]` | `[]`,由 `defineBlock()` 创建。 |
69
69
  | `builtins` | `false \| string[]` | 省略显示全部;`false` 隐藏全部内置区块;数组筛选并排序。隐藏不影响已有节点渲染。自定义类型追加到后面。 |
70
70
  | `pickImage` | `(context: ImagePickerContext) => Promise<string \| null>` | 图片资源选择器;返回 URL,`null` 取消,抛错显示重试;`context` 提供 `value/label/signal`。 |
71
+ | `templateEngine` | `TemplateEngine` | 模板预览适配器;省略时使用默认基础引擎。 |
71
72
 
72
73
  插件建议模块级声明;依赖状态时用 `useMemo`。保持参数引用稳定可复用配置;内联数组不会重建编辑器或清空文档。配置返回值:
73
74
 
@@ -76,6 +77,7 @@ export function EmailPage() {
76
77
  | `definitions` | 全部注册插件的只读列表,包含隐藏的内置区块。 |
77
78
  | `library` | 按展示顺序排列的可插入区块只读列表。 |
78
79
  | `pickImage` | 注册的资源选择器。 |
80
+ | `templateEngine` | 当前画布、预览和解析导出共用的模板引擎。 |
79
81
  | `createBlock(type)` | 已注册的类型字符串 → 独立默认 `EmailBlockNode`;未知类型抛错。 |
80
82
  | `toHtml(document, options?)` | v4 文档与 `DocumentHtmlOptions` → 完整 HTML;自动使用当前注册表。 |
81
83
  | `validate(document)` | 文档 → `ValidationIssue[]`,不渲染 HTML。 |
@@ -100,7 +102,7 @@ export const coupon = defineBlock({
100
102
  | `type` / `title` | 必填非空字符串,分别为唯一类型与区块库标题。 |
101
103
  | `renderEmail(ctx)` | 必填,返回内部 HTML 字符串或 `EmailMarkup`;`ctx` 包含 `block/controls/style/body/mode/email`。 |
102
104
  | `controls` | 名称到 `EmailControls` 声明的映射,默认 `{}`;自动推导值类型与默认值。 |
103
- | `icon` / `description` | 可选图标(ReactNode 或字符串)及说明。 |
105
+ | `icon` / `description` | 可选图标(ReactNode 或 HTML 字符串)及说明。省略图标时,使用 `title` 去除首尾空白后的第一个字符,保留大小写并按纯文本显示。 |
104
106
  | `override` | 替换内置同名类型时必需设为 `true`。 |
105
107
  | `containerStyles` | 默认提供间距与外观;`false` 全禁用;`{ spacing?: boolean, appearance?: boolean }` 可分别禁用。 |
106
108
  | `styleControls` | 额外样式能力;同 `id` 覆盖默认容器能力。 |
@@ -166,7 +168,9 @@ export const coupon = defineBlock({
166
168
  | `getBlockDefinition(type, definitions?)` | 类型与可选注册表,默认内置定义 | 匹配的运行时定义或 `undefined`。不要修改配置中的冻结定义。 |
167
169
  | `generateId(prefix = "block")` | ID 前缀字符串 | 带随机后缀的字符串;应用仍需保持文档内 ID 唯一。 |
168
170
 
169
- `EmailDocument` 含 `version/body/blocks/subject?`;`EmailBlockNode` 含 `id/type/controls/style?/defaultData?`。只支持 `DOCUMENT_VERSION = 4`,不迁移旧文档。JSON 不包含插件组件与渲染逻辑,重新加载时仍需注册相同类型。
171
+ `EmailDocument` 含 `version/body/blocks/subject?`;`EmailBlockNode` 含 `id/type/controls/style?/defaultData?`。`version: number` 只做格式标识:新建文档写入 `DOCUMENT_VERSION = 4`,接入与导出保留传入值,不根据版本号拒绝文档,也不自动迁移旧结构。JSON 不包含插件组件与渲染逻辑,重新加载时仍需注册相同类型。
172
+
173
+ 初始化、`setDocument()`、`config.validate()` 和 HTML 导出统一检查文档为对象、`blocks` 为数组、每个区块的 `id/type` 为非空字符串、`controls` 为对象;结构不合法时直接抛出 `TypeError`,错误包含字段路径。`subject` 提供时必须为字符串;`body` 提供且非 `null` 时必须为对象,缺省值继续由 `resolveEmailBody()` 补齐。仅有旧 `fields` 而没有 `controls` 的节点不能接入;控件值仍按插件声明归一化与校验。
170
174
 
171
175
  主体设置由画布、预览和邮件 HTML 共用:
172
176
 
@@ -175,7 +179,7 @@ export const coupon = defineBlock({
175
179
  | `textColor` | `#000000`,文字颜色。 |
176
180
  | `backgroundColor` | `#ffffff`,居中内容容器背景;外侧白色。 |
177
181
  | `contentWidth` | `600`,内容最大宽度 px。 |
178
- | `fontFamily` | `Arial, Helvetica, sans-serif`。 |
182
+ | `fontFamily` | `""`,检查器显示 `Default`;未指定或为空时,渲染使用 `Arial, Helvetica, sans-serif`。 |
179
183
  | `fontWeight` | `normal`;也可 `bold`。 |
180
184
  | `linkColor` / `linkDecoration` | `#005bd3` / `underline`。 |
181
185
  | `contentPadding` | `0px`,边框内部 CSS padding。 |
@@ -195,6 +199,7 @@ export const coupon = defineBlock({
195
199
  | `resolveHtmlTemplates` | `false` 保留模板;`true` 解析预览值。 |
196
200
  | `variables` | `[]`,变量原文与预览文本。 |
197
201
  | `templateData` | 可选 `TemplateData`,全局对象或列表数据。 |
202
+ | `templateEngine` | 可选 `TemplateEngine`;省略时使用默认基础引擎。外部引擎负责完整语义和 HTML 转义,输出不会被重复转义。 |
198
203
 
199
204
  数据按顶层浅合并,优先级:节点 `defaultData` > `templateData` > `variables`。默认导出仅给与 `variables[].name` 精确匹配且缺点的 token 补点,并保留空格,例如 `{{ store_name }}` → `{{ .store_name }}`;列表外的 Go template 保持原样。变量 `description` 为空时不显示副文案,`value` 只用于预览。
200
205
 
@@ -208,12 +213,19 @@ export const coupon = defineBlock({
208
213
  | `buildRenderContext(node, mode, body = DEFAULT_EMAIL_BODY, definition = 内置查找结果)` | mode 必填;自定义区块需显式传入 definition | 独立归一化 `BlockRenderContext`,不渲染。 |
209
214
  | `variablesToTemplateData(variables)` | 必填 `Variable[]` | 以 name 路径为键、value 为值的 `TemplateData`;支持嵌套路径,忽略无 value 的项。 |
210
215
  | `mergeTemplateData(variables, defaultData?)` | 变量数组与可选对象 | 顶层合并,defaultData 覆盖变量;两者均无数据时返回 undefined。 |
211
- | `renderTemplatePreview(html, defaultData?)` | HTML 字符串与可选预览对象 | 支持部分字段路径、range、if、with 等 Go template 语法;省略数据时保留原文。 |
216
+ | `renderTemplatePreview(template, data?, options?)` | 模板、预览数据和可选 `templateEngine` | 使用注入引擎渲染;默认引擎支持基础字段路径、`range`、`if`、`with`,遇到不支持的表达式时保留完整输入。 |
217
+ | `validateTemplate(template, options?)` | 模板与可选 `templateEngine` | 显式调用外部引擎校验;未提供校验器时返回 `undefined`,不会自动阻止保存。 |
218
+
219
+ `DocumentHtmlOptions` 和 `BlockRenderOptions` 都含 `templateEngine?: TemplateEngine`。`BlockRenderOptions` 含 `resolveHtmlTemplate?: boolean`(注意单数)、`templateData?: TemplateData`、`variables?: Variable[]`、`body?: EmailBodySettings`。`body` 默认 `DEFAULT_EMAIL_BODY`;模板默认不解析。单区块预览取 `templateData ?? node.defaultData`,不会自动合并 variables;需要时显式调用 `mergeTemplateData()`。第四个参数必须为对象,传 boolean 或 null 会抛出 TypeError。
212
220
 
213
- `BlockRenderOptions` 含 `resolveHtmlTemplate?: boolean`(注意单数)、`templateData?: TemplateData`、`variables?: Variable[]`、`body?: EmailBodySettings`。`body` 默认 `DEFAULT_EMAIL_BODY`;模板默认不解析。单区块预览取 `templateData ?? node.defaultData`,不会自动合并 variables;需要时显式调用 `mergeTemplateData()`。第四个参数必须为对象,传 boolean 或 null 会抛出 TypeError。
221
+ `config.toHtml()` 默认使用配置中的引擎,`options.templateEngine` 可按次覆盖。只传 `config.definitions` 不会携带模板引擎配置。
214
222
 
215
223
  `mode` 表示消费场景:`canvas` 供画布,`email` 执行邮件控件校验;它不决定是否解析模板。模板预览不是完整 Go 引擎,也不是 HTML 消毒器或安全沙箱。
216
224
 
225
+ ### 自定义模板引擎
226
+
227
+ 完整说明与接入示例见 [模板预览与自定义引擎](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.zh-CN.md)。
228
+
217
229
  ### renderEmail 中的 email 工具
218
230
 
219
231
  | 方法 | 参数与输出 |
@@ -250,7 +262,7 @@ Props 及 `RichText.inherited` 的字段说明包含在发布包的 `.d.ts` 注
250
262
  | --- | --- |
251
263
  | `BLOCK_TYPES` / `BuiltinBlockType` | heading、text、button、image、divider、spacer、html。 |
252
264
  | `DEFAULT_BLOCKS` | 所有内置 BlockPlugin 定义;自定义注册表使用 config.definitions。 |
253
- | `DEFAULT_EMAIL_BODY` / `DOCUMENT_VERSION` | 默认主体设置 / 文档版本 4。不要直接修改默认对象。 |
265
+ | `DEFAULT_EMAIL_BODY` / `DOCUMENT_VERSION` | 默认主体设置 / 新建文档的格式标识 4。不要直接修改默认对象。 |
254
266
  | `FONT_FAMILIES` | 只读字体选项 `{ label, value }[]`。 |
255
267
  | `STYLE_GROUPS` / `StyleGroup` | general/dimension/typography/decorations 的标识及展示名称。 |
256
268
  | `BORDER_STYLES` / `BorderStyle` | none/solid/dashed/dotted。 |
@@ -264,12 +276,14 @@ Props 及 `RichText.inherited` 的字段说明包含在发布包的 `.d.ts` 注
264
276
  ## 开发与本地联调
265
277
 
266
278
  ```bash
267
- pnpm typecheck
268
- pnpm lint
269
- pnpm build
279
+ npm run typecheck
280
+ npm run lint
281
+ npm run build
270
282
  node --test tests/*.test.mjs tests/*.test.ts
271
283
  ```
272
284
 
285
+ `npm run check` 按上述顺序执行全部检查;只运行 Node 测试时,构建后执行 `npm test`。本仓库默认使用 Node 24 LTS 和 npm 11.13.0。打包检查与发布流程见[发布指南](./release.zh-CN.md)。
286
+
273
287
  先构建再运行依赖 dist 的测试;TypeScript 内部测试需要 Node 22.18+。包提供 ESM、类型与展开后的 CSS。React/React DOM 为 peer dependencies;TipTap、dnd-kit、CodeMirror、Lezer 等为 dependencies,构建时 external,不内联到发布包。
274
288
 
275
- 消费方可使用 `link:../react-email-editor`;先 `pnpm build`,再 `pnpm build:watch` 保持 dist 更新。两项目需共用一个 React 实例:Vite 可设置 `resolve.dedupe: ["react", "react-dom"]`;Next.js 可设置 `transpilePackages: ["@standhigher/react-email-editor"]`,Turbopack 的 root 需覆盖链接目录。
289
+ 消费方可运行 `npm install ../react-email-editor`;先 `npm run build`,再 `npm run build:watch` 保持 dist 更新。两项目需共用一个 React 实例:Vite 可设置 `resolve.dedupe: ["react", "react-dom"]`;Next.js 可设置 `transpilePackages: ["@standhigher/react-email-editor"]`,Turbopack 的 root 需覆盖链接目录。
@@ -0,0 +1,169 @@
1
+ # Changelog
2
+
3
+ English | [简体中文](./changelog.zh-CN.md)
4
+
5
+ ## 0.5.1 — 2026-10-29
6
+
7
+ ### Fixes and improvements
8
+
9
+ - Accept documents according to their actual structure without requiring `version === 4`. Widen `EmailDocument.version` to `number`, preserving the supplied value through initialization, replacement, snapshots, reset and export; new documents still use `4`.
10
+ - Share structural checks across initialization, document replacement, validation and HTML export for the document object, `blocks` array and block `id/type/controls`. Invalid structure throws a `TypeError` with the field path. Preserve body normalization and plugin control defaults without automatically migrating nodes that only contain legacy `fields`.
11
+ - Update both API references and custom block guides. Require automatic updates to both changelogs before commits and choose major, minor, patch or no version bump according to the changes; keep the npm package version independent of the document format identifier.
12
+ - Bump the npm package from `0.5.0` to `0.5.1` as a patch correcting document loading restrictions, without changing the persisted document structure.
13
+
14
+ ### Validation
15
+
16
+ - Type checking, Oxlint, the build and all 127 Node tests pass, covering different document versions, invalid structures, plugin validation, template preview, snapshot isolation, subscriptions and reset.
17
+ - `git diff --check` passes. No UI or styles changed, so browser validation was not run.
18
+
19
+ ## 0.5.0 — 2026-10-29
20
+
21
+ ### Template preview and custom engines
22
+
23
+ - Add a synchronous `TemplateEngine` adapter through `useEmailEditorConfig({ templateEngine })`, sharing external rendering across canvas, editor preview and resolved export. `renderTemplatePreview()`, document rendering and single-block helpers accept the same `templateEngine` option.
24
+ - Export `defaultTemplateEngine` and explicitly invoked `validateTemplate()` without automatically blocking saving. Engine updates preserve documents and instances, instances remain isolated, and engines receive detached data copies.
25
+ - Support basic variables, root context and `if/range/with` in the default engine; preserve the entire render input when syntax is unsupported or control structures are incomplete. Delegate functions, pipelines and local variables to external engines without escaping their output again.
26
+ - Apply the same listed-variable dot normalization to canvas, preview and sending HTML, preserving whitespace and trim markers; JSON retains template source.
27
+ - Fix Unicode field paths and condition truthiness for Date, Map, Set and plain objects. Normalize non-`Error` render and validation exceptions while preserving the original `cause`, with error display, Retry and recovery after replacing an engine.
28
+ - Add dedicated English and Chinese template-engine documentation and integration examples; keep only documentation links in the READMEs.
29
+
30
+ ### Editor defaults
31
+
32
+ - Add and initially select `Default` in BODY Font family, storing `body.fontFamily` as `""` by default. Omitted, empty and whitespace-only values use the internal Arial, Helvetica, sans-serif font stack in the canvas, preview and exported email. Preserve explicit fonts and rich-text formatting; the document version remains `4`.
33
+ - When custom blocks omit `icon`, use the first Unicode code point of the trimmed `title` as the default icon and escape HTML special characters. Explicit icons retain precedence.
34
+
35
+ ### Toolchain and documentation
36
+
37
+ - Migrate project package management from pnpm to npm 11.13.0, replacing `pnpm-lock.yaml` with `package-lock.json` while preserving all direct dependency versions.
38
+ - Use `npm install` and `npm run dev` for local development and `npm ci` for locked installs. Switch validation, automatic fixes, packaging and pre-publish checks to npm, and remove pnpm-specific configuration.
39
+ - Update installation and command instructions in both READMEs, API development and local linking documentation, release guides and project collaboration rules.
40
+ - Limit READMEs to basic integration examples and documentation navigation; maintain feature details, defaults and behavior descriptions in `docs/`.
41
+
42
+ ### Validation
43
+
44
+ - `npm ci` succeeds; `npm run check` passes type checking, Oxlint, build and all 123 Node tests, covering engine selection, context, escaping, data isolation, variable normalization, error recovery and body default-font fallback and switching.
45
+ - `npm run dev` starts successfully; the homepage and `main.tsx` entry module both return HTTP 200.
46
+ - Verify canvas and preview variable output, non-`Error` exception display, Retry and recovery after replacing engines in Chrome; `git diff --check` passes.
47
+ - Verify initial BODY `Default` selection, explicit font switching and restoring the default in Chrome, with matching default fonts in the canvas, HTML-block iframe and desktop/mobile email previews.
48
+
49
+ ## 0.4.3 — 2026-10-28
50
+
51
+ ### Fixes and improvements
52
+
53
+ - Reuse the same Personalization menu in the subject header and configuration panel, with consistent variable colors, width, border radius, shadows and hover styles.
54
+ - Move variable triggers for text, multiline text and link controls to an input suffix on the right, reserving space for content; restore input focus and cursor position after inserting a variable.
55
+ - Open menus to the left or above according to available viewport space, using native popovers to avoid scroll clipping and positioning offsets in embedded containers with `transform`.
56
+ - Complete focus handling when opening from the keyboard, Tab/Shift+Tab exit, Escape dismissal and outside-click dismissal; preserve the selected block on close and clean up observers and event listeners.
57
+ - Keep the public API, email export and `version: 4` document format unchanged.
58
+
59
+ ### Validation
60
+
61
+ - `pnpm check` passed, including type checking, Oxlint, build and all 103 Node tests; add regression coverage for menu positioning, focus, dismissal and lifecycle.
62
+ - Reuse the existing development server for browser interaction checks, covering suffix clicks, cursor insertion, keyboard navigation and positioning inside containers with `transform`.
63
+
64
+ ## 0.4.2 — 2026-10-28
65
+
66
+ ### npm publishing and documentation
67
+
68
+ - Add the MIT license, repository, homepage and issue tracker, pin the public npm registry and pnpm 10.16.1, and recommend Node 24 LTS through `.nvmrc`.
69
+ - Add `pnpm test`, `pnpm check` and complete validation before publishing; preserve CSS through `sideEffects` and verify that consumers can remove unused JavaScript imports.
70
+ - Fix broken npm README images by using fixed absolute CDN URLs for images and viewing links from the published 0.4.1 package; newer packages retain Markdown and SVG documentation without shipping PNG originals again.
71
+ - Use absolute GitHub URLs for language switches, API references, examples, release guides, changelogs and section navigation in both READMEs, and verify target documents and anchors; remote documents must be pushed with the commit.
72
+ - Add English and Chinese release guides, package-content checks and consumer CSS validation; keep the React 18 peer range, existing public API and document format unchanged.
73
+
74
+ ## 0.4.1 — 2026-10-28
75
+
76
+ ### Documentation and type hints
77
+
78
+ - Simplify both READMEs, add module architecture diagrams, and move detailed integration instructions into separate API reference, basic example and advanced example documents.
79
+ - Add examples for saving and restoring documents, body settings and variables, multiple-instance subscriptions, custom blocks, multiple rich-text regions, image resource pickers and individual block template previews; update navigation in the custom block guides.
80
+ - Add English JSDoc for editor instances, document models, plugins and controls, email rendering helpers, rich text and basic UI components, shipped with TypeScript declarations for IDE hover details on parameters, defaults and behavior boundaries.
81
+ - Update only documentation, type comments and the package version, without changing runtime behavior, the public API or the persisted document version; documents remain at `version: 4`.
82
+
83
+ ### Validation
84
+
85
+ - Type checking, Oxlint, build and all 96 Node tests passed.
86
+ - All 34 TypeScript/TSX example files from both READMEs, API references and scenario documents passed type checking; all local documentation link targets exist.
87
+
88
+ ## 0.4.0 — 2026-10-28
89
+
90
+ ### Features and improvements
91
+
92
+ - Standardize React integration on `useEmailEditorConfig()`, `useEmailEditor()` and `<EmailEditor editor={editor}>`; add `useEmailEditorState(editor)` for on-demand subscriptions to cached, read-only document snapshots.
93
+ - Keep editor instance references stable and isolated, allowing reading, validation and export without mounting the UI; add `setDocument()` and `reset()`, clearing selection, focus and control state from the previous document on replacement or reset.
94
+ - Preserve the current document when configuration or variables change; merge external body input when its value changes, avoid initialization callbacks, and keep document reads and callback arguments as independent snapshots.
95
+ - Add export, new-document, reset and subscription examples to the playground, and update English and Chinese integration documentation.
96
+
97
+ ### Fixes and validation
98
+
99
+ - Synchronize configuration, variables, body settings and callbacks during commit so child layout effects can read new values; defer subscription notifications and body callbacks to the layout phase, keeping interrupted concurrent renders from changing existing instances.
100
+ - Use instance-specific IDs for subject inputs so labels, inputs and character-count hints remain independently associated across multiple editors.
101
+ - Add coverage for configuration update timing, concurrent interruption, subject associations across multiple instances, negative type contracts for removed APIs and checks for compatibility entry points in source.
102
+
103
+ ### Breaking changes
104
+
105
+ - Remove public `createEmailEditorConfig`, `EmailEditorConfigOptions`, `EmailEditorHandle`, component refs and previous state Props, without aliases or compatibility layers. Pass configuration and state to `useEmailEditor()`; the component accepts only `editor` and `height`.
106
+ - The fourth argument of `blockToHtml()` and `renderBlockContent()` accepts only a `BlockRenderOptions` object; remove boolean adaptation and explicitly reject booleans passed from JavaScript. Template previews use `{ resolveHtmlTemplate: true }`.
107
+ - Persisted documents remain at `version: 4`, without migration of older versions; this package version change affects only the package API and behavior.
108
+
109
+ ## 0.3.0 — 2026-10-28
110
+
111
+ ### Features and improvements
112
+
113
+ - Add `defineBlock()`, `EmailControls` and `createEmailEditorConfig()`, sharing plugin configuration between the editor and standalone email export; support filtering, sorting and explicit overrides in the built-in block library.
114
+ - Support custom control components, React Canvas/Inspector components, multiple `EditableRichText` regions, `ControlEditor`, declarative nested lists, image resource pickers and shared email HTML helpers in block plugins.
115
+ - Add `config.validate()` to collect issues with numeric ranges, options, URL/image protocols, list counts and business validation; support updating several related controls at once through `setControls()`.
116
+ - Add English and Chinese custom block API documentation, coupon and product card examples, and component regression tests.
117
+
118
+ ### Fixes
119
+
120
+ - Preserve explicit colors and underlines when changing link URLs or opening behavior, and restore BODY inheritance on reset.
121
+ - Use the same base typography for the canvas body, preview and email export, fixing differences in calculations for `em` padding, borders and other dimensions.
122
+ - Fix asynchronous list updates overwriting subsequent edits or sorting; merge the latest values by stable row identifiers, ignore stale results after deletion or unmount, and avoid reusing deleted row identifiers for new rows.
123
+ - Give custom controls independent snapshots of values, body settings and variables to prevent parameter mutations from affecting the host; isolate variable synchronization for custom Canvas/Inspector components as well.
124
+ - Show an empty option in optional selects and support clearing them; associate control labels correctly with selects.
125
+ - Make inspector rich text inherit body settings and enabled block typography, paragraph, list and link styles, updating when body configuration changes.
126
+ - Report missing plugins and email export failures explicitly; isolate canvas and inspector errors per block and preserve original node data.
127
+
128
+ ### Breaking changes
129
+
130
+ - Switch custom blocks to `defineBlock` + `EmailControls` + `createEmailEditorConfig`, with the component receiving `config`; remove the old `blocks` Prop, public `BlockDefinition/FieldConfig/FieldType` and `resolveBlockStyleFields`, without an adaptation layer. Control factories use `EmailControls.text(...)`, and blocks and lists declare controls through `controls` (without `field` / `fields` aliases); runtime and persisted nodes use `controls`, style declarations use `styleControls`, and update methods use `setControl/setControls`, with corresponding renames for types, `ControlEditor` and `controlPath`. Content defaults come from control declarations, rendering uses `renderEmail`, and the document format advances to v4, without compatibility or automatic migration for old data.
131
+ - Standardize email body settings on `body`: documents use `EmailDocument.body`, the component uses `body` / `onBodyChange`, and types and helpers use `EmailBodySettings`, `DEFAULT_EMAIL_BODY` and `resolveEmailBody`; block rendering contexts and `BlockRenderOptions` also use `body`. Remove the previous Global API without aliases or migration code.
132
+ - Advance the document version to `4`, without support for older versions. BODY General provides text color, link color, font, font weight and a link underline toggle; Content provides a single body Background, content width, Padding and block spacing, with border color, width and style grouped under Advanced: Border, collapsed by default. Apply configuration consistently to canvas, preview and export while preserving explicit content formatting. Keep only `contentPadding`, removing `outerPadding`, body alignment, outer background and border radius; center the body and keep the outer area white.
133
+
134
+ ## 0.2.0 — 2026-10-27
135
+
136
+ ### Breaking changes
137
+
138
+ - Advance the document format from `version: 1` to `version: 2`, rejecting older versions without automatic migration. Use `createEmailDocument()` to create current-version documents; upgrading existing data requires updating block fields and styles as well as the version number.
139
+ - Store text formatting for Heading, Text and Button in `fields.html`; Button no longer reads `fields.label` or typography from `style.element`. `EmailElementStyle` no longer extends `EmailTypographyStyle`.
140
+ - Make built-in blocks declare `styleFields` according to their capabilities and ignore disabled styles; remove `dimension.height` and block-level `typography` from built-in blocks. Custom blocks can still enable these fields explicitly, with `exactStyleFields` constraining defaults, canvas and email output.
141
+ - Preserve paragraphs, lists, links and explicit styles inside HTML block source without injecting body typography; undeclared properties can still inherit global styles.
142
+
143
+ ### Features and improvements
144
+
145
+ - Support inline rich-text editing for Button with a single paragraph, joining multiple pasted paragraphs with spaces; preserve basic inline formatting in email output and generate only one outer button link.
146
+ - Add `BlockInspectorSection`, `inspectorSections`, `RichTextProfile` and `inlineEditProfile` for custom inspector groups and rich-text capabilities; standalone `RichText` supports `profile`.
147
+ - Unify color panels, HEX input and color reset; use a centered dialog for link editing, with color, underline and opening behavior options.
148
+ - Improve block action bars, drag feedback, the three-column layout, toolbar wrapping and preview height; let previews follow document content.
149
+ - Change the default button fill to black and default button/image links to empty, with no navigation for blank links; default global content, block, button and image borders to transparent.
150
+ - Simplify both READMEs, add npm keywords, and include the Chinese README and changelog in the published package.
151
+
152
+ ### Fixes and validation
153
+
154
+ - Fix cursor, selection and formatting preservation after rich-text formatting operations, and preserve spacing and indentation when switching lists.
155
+ - Fix button export handling of quotes, comparison operators, comments and spaces inside template expressions.
156
+ - Add Oxlint and regression tests for block configuration, rich text, lists, buttons and color conversion.
157
+
158
+ ## 0.1.0 — 2026-10-27
159
+
160
+ First official release (commit `48011d9`).
161
+
162
+ - Provide a React 18 editor, draggable blocks, rich-text editing, an inspector and desktop/mobile previews.
163
+ - Support JSON documents, global and block styles, HTML source editing, custom blocks and Go template previews.
164
+ - Provide data access through component refs, standalone HTML rendering, ESM, TypeScript declarations and a separate CSS entry point.
165
+ - Provide a Vite playground, build tooling and a local linking workflow.
166
+
167
+ ## 0.0.0-stage — 2026-10-27
168
+
169
+ - Package distribution verification version before the first release.
@@ -1,5 +1,76 @@
1
1
  # 更新日志
2
2
 
3
+ [English](./changelog.md) | 简体中文
4
+
5
+ ## 0.5.1 — 2026-10-29
6
+
7
+ ### 修复与改进
8
+
9
+ - 文档接入改为检查实际结构,不再强制要求 `version === 4`;`EmailDocument.version` 类型放宽为 `number`,初始化、替换、快照、重置及导出保留传入值,新建文档仍写入 `4`。
10
+ - 初始化、文档替换、校验和 HTML 导出统一检查文档对象、`blocks` 数组及区块的 `id/type/controls`,非法结构抛出包含字段路径的 `TypeError`;保留主体默认值归一化和插件控件默认值,不自动迁移仅有旧 `fields` 的节点。
11
+ - 同步中英文 API 与自定义区块文档,并规定提交前自动更新中英文 changelog、按变更影响决定 major、minor、patch 或不升级版本;npm 包版本与文档格式标识独立。
12
+ - 本次以 patch 修正文档接入限制,npm 包版本从 `0.5.0` 升至 `0.5.1`,不改变文档持久化结构。
13
+
14
+ ### 验证
15
+
16
+ - 类型检查、Oxlint、构建及全部 127 项 Node 测试通过,覆盖不同版本接入、非法结构、插件校验、模板预览、快照隔离、订阅与重置。
17
+ - `git diff --check` 通过;本次未修改 UI 或样式,未启动浏览器验证。
18
+
19
+ ## 0.5.0 — 2026-10-29
20
+
21
+ ### 模板预览与自定义引擎
22
+
23
+ - 新增同步 `TemplateEngine` 适配器,通过 `useEmailEditorConfig({ templateEngine })` 让画布、编辑器预览和解析导出共用外部引擎;`renderTemplatePreview()`、文档及单区块渲染支持相同的 `templateEngine` 选项。
24
+ - 导出 `defaultTemplateEngine` 与按需调用的 `validateTemplate()`;语法校验不会自动阻止保存。引擎更新保留文档与实例,多实例之间独立,传入引擎的数据为隔离副本。
25
+ - 默认引擎支持基础变量、根上下文及 `if/range/with`;包含未支持语法或不完整控制结构时保留该次渲染的完整输入。函数、管道及局部变量交由外部引擎处理,外部输出不重复转义。
26
+ - 统一画布、预览和发送 HTML 的变量清单补点规则,保留空白与 trim 标记;JSON 保存模板原文。
27
+ - 修复 Unicode 字段路径解析,以及 Date、Map、Set 和普通对象的条件真值判断;渲染与校验将非 `Error` 异常归一化并保留原始 `cause`,支持错误提示、Retry 及更换引擎恢复。
28
+ - 新增中英文模板引擎独立文档与接入示例,README 仅保留文档引用。
29
+
30
+ ### 编辑器默认行为
31
+
32
+ - BODY 的 Font family 新增 `Default` 并默认选中,`body.fontFamily` 默认保存为 `""`;未指定、空字符串及纯空白值在画布、预览和邮件导出时使用内部默认字体 `Arial, Helvetica, sans-serif`。保留显式字体与富文本格式,文档版本仍为 `4`。
33
+ - 自定义区块省略 `icon` 时,使用 `title` 去除首尾空白后的首个 Unicode 码点作为默认图标,并转义 HTML 特殊字符;显式图标保持优先。
34
+
35
+ ### 工具链与文档
36
+
37
+ - 项目包管理器从 pnpm 迁移为 npm 11.13.0,使用 `package-lock.json` 替换 `pnpm-lock.yaml`,保持所有直接依赖版本不变。
38
+ - 开发启动统一使用 `npm install` 和 `npm run dev`;固定锁文件安装使用 `npm ci`。检查、自动修复、打包和发布前校验脚本均改为 npm,移除 pnpm 专属配置。
39
+ - 同步中英文 README、API 开发与本地联调说明、发布指南及项目协作规则中的安装和命令说明。
40
+ - 明确 README 只保留基本接入示例和文档导航,功能细节、默认值与行为说明统一维护在 `docs/`。
41
+
42
+ ### 验证
43
+
44
+ - `npm ci` 安装成功;`npm run check` 通过类型检查、Oxlint、构建及全部 123 项 Node 测试,覆盖模板引擎选择、上下文、转义、数据隔离、变量补点、异常恢复及主体默认字体回退与切换。
45
+ - `npm run dev` 启动成功,首页和 `main.tsx` 入口模块均返回 HTTP 200。
46
+ - Chrome 验证画布与预览的变量结果,以及非 `Error` 异常提示、Retry 和更换引擎恢复;`git diff --check` 通过。
47
+ - Chrome 验证主体默认选中 `Default`、显式字体切换及恢复默认,核对画布、HTML 区块 iframe 和桌面/移动邮件预览的默认字体一致。
48
+
49
+ ## 0.4.3 — 2026-10-28
50
+
51
+ ### 修复与改进
52
+
53
+ - 主题 header 与配置区复用同一个 Personalization 菜单,统一变量颜色、宽度、圆角、阴影和 hover 样式。
54
+ - 将配置区文本、多行文本与链接字段的变量入口移至输入框右侧后缀,预留内容空间;插入变量后恢复输入焦点和光标位置。
55
+ - 菜单按视口空间向左或向上展开,使用原生 popover 避免滚动裁切及带 `transform` 的嵌入容器引起的定位偏移。
56
+ - 补齐键盘打开时的焦点、Tab/Shift+Tab 退出、Escape 关闭与外部点击关闭;关闭菜单时保留当前区块选择,并清理观察器及事件监听器。
57
+ - 保持公开 API、邮件导出和 `version: 4` 文档格式不变。
58
+
59
+ ### 验证
60
+
61
+ - `pnpm check` 通过,包含类型检查、Oxlint、构建及全部 103 项 Node 测试;新增菜单定位、焦点、关闭与生命周期回归测试。
62
+ - 复用现有开发服务完成浏览器交互验收,覆盖后缀点击、光标插入、键盘导航及带 `transform` 容器中的定位。
63
+
64
+ ## 0.4.2 — 2026-10-28
65
+
66
+ ### npm 发布与文档
67
+
68
+ - 补齐 MIT 许可证、仓库、主页和 Issue 地址,固定公开 npm registry 与 pnpm 10.16.1,并通过 `.nvmrc` 推荐 Node 24 LTS。
69
+ - 新增 `pnpm test`、`pnpm check` 和发布前完整校验;通过 `sideEffects` 保留 CSS,并验证消费方可以移除未使用的 JavaScript 导入。
70
+ - 修复 npm README 图片失效:中英文图片与查看链接改用已发布 0.4.1 资源的固定绝对 CDN 地址;新版包不重复携带 PNG 原图,保留 Markdown 和 SVG 文档。
71
+ - 中英文 README 的语言切换、API、示例、发布指南、更新日志及章节导航统一使用 GitHub 绝对地址,并验证目标文档和锚点存在;远端文档需随提交一并推送。
72
+ - 补充中英文发布指南、发布文件范围与 CSS 消费验证;保持 React 18 peer 范围及现有公开 API、文档格式不变。
73
+
3
74
  ## 0.4.1 — 2026-10-28
4
75
 
5
76
  ### 文档与类型提示