@caikengren/tang-svg-editor
v0.1.0
Published
A host-agnostic multi-page SVG editor for React
Maintainers
Readme
@caikengren/tang-svg-editor
面向浏览器的多页 SVG 编辑器(React 组件)。它只负责 SVG 文档的编辑、标注、导入、撤销重做和显式保存;文件系统、持久化、路由和发布流程由宿主应用负责。
特性
- 多页 SVG 编辑:页面新增、复制、删除、重排;
viewBox作为画布尺寸事实源。 - 完整编辑能力:撤销/重做、复制/剪切/粘贴/重复、全选、组合/取消组合、对齐、图层与页面操作。
- 元素标注:绑定到一个或多个有 ID 的 SVG 元素,随文档导出为
data-edit-target/data-edit-annotation。 - 原子多 SVG 导入:按自然文件名排序,任一文件无效时整批不提交。
- 安全清洗:导入、素材与 AI 结果统一经过
sanitizeSvg/normalizeSvgForEditing,移除脚本、事件属性与危险 URL。 - SVG/PNG 导出:
exportPageSvg、exportPagePng与renderSvgToPngBlob。 - 宿主无关:通过
AssetAdapter/AIAdapter/ExportAdapter和 feature flags 对接宿主能力,不绑定具体平台。 - 零构建接入:
./browser入口支持 import map + 公共 CDN 直接使用。
安装
npm install @caikengren/tang-svg-editor
# 或
pnpm add @caikengren/tang-svg-editor
# 或
yarn add @caikengren/tang-svg-editorReact 接入
import { useState } from 'react'
import {
TangSvgEditor,
createBlankDocument,
type TangSvgDocument,
} from '@caikengren/tang-svg-editor'
import '@caikengren/tang-svg-editor/style.css'
export function EditorPage() {
const [document, setDocument] = useState<TangSvgDocument>(() => createBlankDocument())
return (
<div style={{ height: '100vh' }}>
<TangSvgEditor
value={document}
onChange={(next, change) => {
setDocument(next)
console.log(change.source, change.operations)
}}
onSave={async (snapshot, intent) => {
await saveCompleteDocument(snapshot)
return {
status: 'saved',
savedRevision: intent.revision,
savedAt: Date.now(),
message: '保存成功',
}
}}
/>
</div>
)
}容器必须有明确高度。onChange 在一次用户操作提交后触发一次,参数同时包含完整文档与精确的 operations;拖拽在释放时提交,不逐帧通知宿主。
onSave 只由保存按钮、Ctrl/Cmd+S 或命令式 save() 触发。它收到全部页面的完整 SVG;编辑器会移除内部 data-svg-editor-* 属性,并保留 data-edit-target 与 data-edit-annotation。保存期间又发生修改时,较旧的保存响应不会清除新 dirty 状态。
常用 props
| 属性 | 作用 |
| --- | --- |
| value / defaultValue | 受控 / 非受控文档,二选一 |
| onChange(value, change) | 每个可撤销 transaction 提交后回调完整快照 |
| onSave(value, intent) | 保存按钮或快捷键触发的显式保存意图 |
| onBack() / onError(error) | 可选宿主返回行为与标准化错误 |
| features | 隐藏 assets / ai / import / export 内建能力 |
| headerActions | 自定义 header 按钮 |
| readOnly | 禁用写操作,保留预览、缩放和导出 |
| uiTheme | light、dark 或 system |
| assets / ai / exporter | 可选宿主能力适配器 |
受控宿主原样回传最近一次 onChange 的对象时,编辑器保留本地历史;传入其他对象被视为外部替换,会清理选择并重置历史。
Header 按钮配置
tse-header 默认按 撤销 → 重做 → 保存 → 导入 SVG 渲染,对应 DEFAULT_EDITOR_HEADER_ACTIONS(['undo', 'redo', 'save', 'import'])。传入 headerActions 会完整替换默认配置;内置动作可按 ID 复用,也可插入宿主按钮:
import {
DEFAULT_EDITOR_HEADER_ACTIONS,
TangSvgEditor,
} from '@caikengren/tang-svg-editor'
const actions = [
...DEFAULT_EDITOR_HEADER_ACTIONS.filter((action) => action !== 'import'),
{
id: 'exit-preview',
label: '退出预览',
onClick: stopPreview,
},
] as const
<TangSvgEditor headerActions={actions} />自定义按钮支持 ariaLabel、title、disabled、className,以及 default、primary、icon 三种 appearance。自定义 onClick 可以返回 Promise;失败会通过 onError 以 header-action-failed 上报。保存和导入动作仍会根据 onSave、feature flags 与只读状态自动判断是否可用。
标注与导入
- 标注绑定到一个或多个有 ID 的 SVG 元素,说明上限为 10,000 字符。
- 标注保存为
data-edit-target="true"和data-edit-annotation="..."。 - “导入 SVG”支持多选,按包含数字的自然文件名顺序处理。
- 同名文件替换现有页面;任一文件无效时整批不提交。
- 可通过 feature flags 禁用页面新增、复制、删除和手动重排。
Browser ESM / CDN 接入
dist/tang-svg-editor.browser.js 是浏览器原生 ESM 入口:它包含编辑器运行时依赖与 CSS,但把 React、ReactDOM 保留为外部依赖。通过 import map 保证宿主和编辑器共享同一份 React:
<div id="editor-root" style="height:100vh"></div>
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/[email protected]",
"react/jsx-runtime": "https://esm.sh/[email protected]/jsx-runtime",
"react-dom": "https://esm.sh/[email protected]?external=react",
"react-dom/client": "https://esm.sh/[email protected]/client?external=react",
"@caikengren/tang-svg-editor/browser": "https://cdn.jsdelivr.net/npm/@caikengren/[email protected]/dist/tang-svg-editor.browser.js"
}
}
</script>
<script type="module">
const { mount } = await import('@caikengren/tang-svg-editor/browser')
const editor = mount(document.getElementById('editor-root'), { value: documentData })
</script>npm 发布后 jsDelivr/unpkg 会自动镜像文件。生产页面应固定完整版本号,不要引用 @latest。
Standalone API
./browser 入口同时导出 mount()、version、DEFAULT_EDITOR_HEADER_ACTIONS、createBlankDocument、normalizeSvgForEditing、sanitizeSvg:
import { mount, version, createBlankDocument } from '@caikengren/tang-svg-editor/browser'mount() 返回的 controller 提供 getDocument、setDocument、save、focus、undo、redo、selectPage 和 destroy。同一页面可以挂载多个实例;样式只注入一次。页面卸载时应调用 destroy()。
快捷键
Ctrl/Cmd+S:保存Ctrl/Cmd+Z:撤销Ctrl/Cmd+Shift+Z或Ctrl/Cmd+Y:重做Ctrl/Cmd+C/X/V/D:复制、剪切、粘贴、重复元素Ctrl/Cmd+A/G/Shift+G:全选、组合、取消组合Delete/Backspace:删除选择PageUp/PageDown:切换页面
兼容性
- React
>=18.2 <20(peer dependency,React 18.2 与 19.x 均已验证)。 - 常规 npm 入口把 React 与 ReactDOM 作为 peer dependencies;Browser ESM 由 import map 提供 React。
- 组件依赖 DOMParser、XMLSerializer、Canvas、Blob 与浏览器事件,不支持服务端渲染;Next.js 应放在 Client Component 中,或使用关闭 SSR 的动态导入。
- 宿主必须导入
style.css,所有选择器与变量使用.tang-svg-editor、.tang-svg-editor-portal与--tse-*命名空间,不输出全局 reset。
开发与发布
# 构建(产出 ESM/CJS + 类型声明 + 浏览器 ESM)
pnpm --filter @caikengren/tang-svg-editor build
# 发布前门禁:tarball 内容、publint、Are The Types Wrong、consumer smoke
pnpm pack:check
pnpm consumer:check发布(Changesets)
pnpm changeset # 1. 记录本次变更类型(patch / minor / major)
pnpm changeset version # 2. 依据 changeset 提升版本号并生成 CHANGELOG(不自动提交)
pnpm release # 3. package:check + consumer:check + changeset publishprepublishOnly 会在 changeset publish 前自动执行 pnpm build,确保发布物是最新构建;发布前记得把第 2 步的版本号变更提交到 git。
