From d099db414064a6bb9bce397919846b5f07e97131 Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 23 Jun 2026 18:39:38 +0800 Subject: [PATCH] refactor(tiptap): move coordinator to editor.storage, drop dead Markdown html option --- libs/tiptap-editor/src/index.ts | 18 ++++++++---------- libs/tiptap-editor/src/upload-coordinator.ts | 3 +++ libs/tiptap-editor/src/upload-image.ts | 18 ++++++------------ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/libs/tiptap-editor/src/index.ts b/libs/tiptap-editor/src/index.ts index ed7d11c..8e54e40 100644 --- a/libs/tiptap-editor/src/index.ts +++ b/libs/tiptap-editor/src/index.ts @@ -5,8 +5,8 @@ import { TableKit } from '@tiptap/extension-table' import { TaskList, TaskItem } from '@tiptap/extension-list' import { FileHandler } from '@tiptap/extension-file-handler' import { SlashCommand } from './slash-command' -import { UploadCoordinator, type UploadEvent } from './upload-coordinator' -import { UploadImage, setUploadCoordinator } from './upload-image' +import { UploadCoordinator, UPLOAD_COORDINATOR_STORAGE_KEY, type UploadEvent } from './upload-coordinator' +import { UploadImage } from './upload-image' import './style.css' export interface EditorOptions { @@ -82,9 +82,7 @@ class TiptapEditorInstance { HTMLAttributes: { rel: 'noopener noreferrer', target: '_blank' }, }, }), - Markdown.configure({ - html: false, - }), + Markdown, TableKit, UploadImage, TaskList, @@ -131,15 +129,16 @@ class TiptapEditorInstance { }, }) - // 创建上传协调器,注入给 NodeView 的 onRetry/onRemove - // onUploadEvent 透传给 coordinator.emit,未提供时空操作兜底 + // 创建上传协调器,挂到 editor.storage 供 NodeView 按实例读取(支持多编辑器实例)。 + // onUploadEvent 透传给 coordinator.emit,未提供时空操作兜底。 if (this.options.onImageUpload) { this.coordinator = new UploadCoordinator( this.editor, this.options.onImageUpload, this.options.onUploadEvent ?? (() => {}), ) - setUploadCoordinator(this.coordinator) + // editor.storage 是开放式索引签名;自定义 key 需绕过严格检查(Tiptap 官方扩展 storage 模式)。 + ;(this.editor.storage as unknown as Record)[UPLOAD_COORDINATOR_STORAGE_KEY] = this.coordinator } // 通知宿主编辑器已就绪(替代 window.__tiptap_ready 轮询) @@ -245,9 +244,8 @@ class TiptapEditorInstance { destroy(): void { this.editor?.destroy() this.editor = null + // coordinator 通过 editor.storage 访问,随 editor 实例一同回收,无需显式清除引用。 this.coordinator = null - // 清除 NodeView 的 coordinator 引用,避免指向已销毁的实例 - setUploadCoordinator(null) // 清理源码模式相关引用(容器 innerHTML 已清空,DOM 会随之移除) this.sourceTextarea = null this.toggleButton = null diff --git a/libs/tiptap-editor/src/upload-coordinator.ts b/libs/tiptap-editor/src/upload-coordinator.ts index 878446b..b29071a 100644 --- a/libs/tiptap-editor/src/upload-coordinator.ts +++ b/libs/tiptap-editor/src/upload-coordinator.ts @@ -1,5 +1,8 @@ import type { Editor } from '@tiptap/core' +/** editor.storage 上挂载 coordinator 的 key。NodeView 通过此 key 拿到当前编辑器实例的 coordinator。 */ +export const UPLOAD_COORDINATOR_STORAGE_KEY = 'uploadCoordinator' + /** pending 上传条目:保留 File 供重试,blobUrl 供本地预览,state 跟踪当前态。 */ interface UploadEntry { file: File diff --git a/libs/tiptap-editor/src/upload-image.ts b/libs/tiptap-editor/src/upload-image.ts index 945fb40..d182578 100644 --- a/libs/tiptap-editor/src/upload-image.ts +++ b/libs/tiptap-editor/src/upload-image.ts @@ -1,6 +1,6 @@ import { Image } from '@tiptap/extension-image' import type { Node as PMNode } from '@tiptap/pm/model' -import type { UploadCoordinator } from './upload-coordinator' +import { UPLOAD_COORDINATOR_STORAGE_KEY, type UploadCoordinator } from './upload-coordinator' /** NodeView 按钮点击 / destroy 回调注入接口。 */ export interface UploadNodeViewCallbacks { @@ -146,14 +146,6 @@ class UploadImageNodeView { } } -/** coordinator 引用(由 index.ts 在创建 editor 前注入)。 */ -let coordinatorRef: Pick | null = null - -/** index.ts 注入 coordinator,供 NodeView 的 onRetry/onRemove 调用。 */ -export function setUploadCoordinator(c: typeof coordinatorRef): void { - coordinatorRef = c -} - /** * 自定义 Image 扩展:继承父类属性,加三个上传状态属性,用自定义 NodeView。 */ @@ -189,13 +181,15 @@ export const UploadImage = Image.configure({ allowBase64: true }).extend({ }, addNodeView() { - return ({ node, HTMLAttributes }) => { + return ({ node, HTMLAttributes, editor }) => { + const coordinator = (editor.storage as unknown as Record)[UPLOAD_COORDINATOR_STORAGE_KEY] as UploadCoordinator | undefined return new UploadImageNodeView({ node, HTMLAttributes, callbacks: { - onRetry: (id) => coordinatorRef?.retryUpload(id), - onRemove: (id) => coordinatorRef?.removeUpload(id), + onRetry: (id) => coordinator?.retryUpload(id), + onRemove: (id) => coordinator?.removeUpload(id), + onDestroyed: (id) => coordinator?.handleNodeDestroyed(id), }, }) }