@shenjipo/mention-editor 1.0.0 → 2.1.0

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.
@@ -0,0 +1,98 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { EditorState, Plugin, Transaction } from 'prosemirror-state';
3
+ import { EditorView } from 'prosemirror-view';
4
+
5
+ interface ImageInputPayload {
6
+ file: File;
7
+ base64: string;
8
+ width?: number;
9
+ height?: number;
10
+ }
11
+ interface ImageInputOptions {
12
+ onImageInput?: (payload: ImageInputPayload) => void;
13
+ onFileReject?: (file: File) => void;
14
+ }
15
+
16
+ type StringKeyOf<T> = Extract<keyof T, string>;
17
+ type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];
18
+ type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any;
19
+ declare class EventEmitter<T extends Record<string, any>> {
20
+ private callbacks;
21
+ on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): () => void;
22
+ protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): void;
23
+ off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): void;
24
+ protected removeAllListeners(): void;
25
+ }
26
+
27
+ type UiElementPosition = {
28
+ show: boolean;
29
+ referencePos: DOMRect;
30
+ };
31
+
32
+ type SuggestionMenuState = UiElementPosition & {
33
+ query: string;
34
+ ignoreQueryLength?: boolean;
35
+ };
36
+ interface MentionItem {
37
+ id: string;
38
+ label: string;
39
+ }
40
+
41
+ type SuggestionPluginState = {
42
+ triggerCharacter: string;
43
+ deleteTriggerCharacter: boolean;
44
+ queryStartPos: number;
45
+ query: string;
46
+ decorationId: string;
47
+ ignoreQueryLength?: boolean;
48
+ } | undefined;
49
+ declare class SuggestionMenuView {
50
+ private readonly editor;
51
+ pluginState: SuggestionPluginState;
52
+ state?: SuggestionMenuState;
53
+ emitUpdate: (triggerCharacter: string) => void;
54
+ private rootEl?;
55
+ constructor(editor: MEditor, emitUpdate: (menuName: string, state: SuggestionMenuState) => void);
56
+ update(view: EditorView, prevState: EditorState): void;
57
+ destroy(): void;
58
+ closeMenu: () => void;
59
+ clearQuery: () => void;
60
+ insertMention: (item: MentionItem) => void;
61
+ getReplaceRange(): {
62
+ from: number;
63
+ to: number;
64
+ };
65
+ }
66
+ declare class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {
67
+ view: SuggestionMenuView;
68
+ readonly plugin: Plugin;
69
+ private triggerCharacters;
70
+ constructor(editor: MEditor);
71
+ onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void): () => void;
72
+ addTriggerCharacter: (triggerCharacter: string) => void;
73
+ removeTriggerCharacter: (triggerCharacter: string) => void;
74
+ closeMenu: () => void;
75
+ clearQuery: () => void;
76
+ insertMention: (item: MentionItem) => void;
77
+ get shown(): boolean;
78
+ }
79
+
80
+ interface MentionEditorOptions {
81
+ element: HTMLElement;
82
+ onChange: (text: string) => void;
83
+ placeholder?: string;
84
+ onImageInput?: (payload: ImageInputPayload) => void;
85
+ onFileReject?: (file: File) => void;
86
+ }
87
+ declare class MEditor {
88
+ _tiptapEditor: Editor;
89
+ readonly suggestionMenus: SuggestionMenuProseMirrorPlugin;
90
+ constructor(options: MentionEditorOptions);
91
+ dispatch(tr: Transaction): void;
92
+ inserMentionBlock(item: MentionItem): void;
93
+ get isEditable(): boolean;
94
+ get domElement(): HTMLDivElement;
95
+ clear(): void;
96
+ }
97
+
98
+ export { type ImageInputOptions, type ImageInputPayload, type MentionItem, type SuggestionMenuState, MEditor as default };
@@ -0,0 +1,98 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { EditorState, Plugin, Transaction } from 'prosemirror-state';
3
+ import { EditorView } from 'prosemirror-view';
4
+
5
+ interface ImageInputPayload {
6
+ file: File;
7
+ base64: string;
8
+ width?: number;
9
+ height?: number;
10
+ }
11
+ interface ImageInputOptions {
12
+ onImageInput?: (payload: ImageInputPayload) => void;
13
+ onFileReject?: (file: File) => void;
14
+ }
15
+
16
+ type StringKeyOf<T> = Extract<keyof T, string>;
17
+ type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];
18
+ type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any;
19
+ declare class EventEmitter<T extends Record<string, any>> {
20
+ private callbacks;
21
+ on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): () => void;
22
+ protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): void;
23
+ off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): void;
24
+ protected removeAllListeners(): void;
25
+ }
26
+
27
+ type UiElementPosition = {
28
+ show: boolean;
29
+ referencePos: DOMRect;
30
+ };
31
+
32
+ type SuggestionMenuState = UiElementPosition & {
33
+ query: string;
34
+ ignoreQueryLength?: boolean;
35
+ };
36
+ interface MentionItem {
37
+ id: string;
38
+ label: string;
39
+ }
40
+
41
+ type SuggestionPluginState = {
42
+ triggerCharacter: string;
43
+ deleteTriggerCharacter: boolean;
44
+ queryStartPos: number;
45
+ query: string;
46
+ decorationId: string;
47
+ ignoreQueryLength?: boolean;
48
+ } | undefined;
49
+ declare class SuggestionMenuView {
50
+ private readonly editor;
51
+ pluginState: SuggestionPluginState;
52
+ state?: SuggestionMenuState;
53
+ emitUpdate: (triggerCharacter: string) => void;
54
+ private rootEl?;
55
+ constructor(editor: MEditor, emitUpdate: (menuName: string, state: SuggestionMenuState) => void);
56
+ update(view: EditorView, prevState: EditorState): void;
57
+ destroy(): void;
58
+ closeMenu: () => void;
59
+ clearQuery: () => void;
60
+ insertMention: (item: MentionItem) => void;
61
+ getReplaceRange(): {
62
+ from: number;
63
+ to: number;
64
+ };
65
+ }
66
+ declare class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {
67
+ view: SuggestionMenuView;
68
+ readonly plugin: Plugin;
69
+ private triggerCharacters;
70
+ constructor(editor: MEditor);
71
+ onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void): () => void;
72
+ addTriggerCharacter: (triggerCharacter: string) => void;
73
+ removeTriggerCharacter: (triggerCharacter: string) => void;
74
+ closeMenu: () => void;
75
+ clearQuery: () => void;
76
+ insertMention: (item: MentionItem) => void;
77
+ get shown(): boolean;
78
+ }
79
+
80
+ interface MentionEditorOptions {
81
+ element: HTMLElement;
82
+ onChange: (text: string) => void;
83
+ placeholder?: string;
84
+ onImageInput?: (payload: ImageInputPayload) => void;
85
+ onFileReject?: (file: File) => void;
86
+ }
87
+ declare class MEditor {
88
+ _tiptapEditor: Editor;
89
+ readonly suggestionMenus: SuggestionMenuProseMirrorPlugin;
90
+ constructor(options: MentionEditorOptions);
91
+ dispatch(tr: Transaction): void;
92
+ inserMentionBlock(item: MentionItem): void;
93
+ get isEditable(): boolean;
94
+ get domElement(): HTMLDivElement;
95
+ clear(): void;
96
+ }
97
+
98
+ export { type ImageInputOptions, type ImageInputPayload, type MentionItem, type SuggestionMenuState, MEditor as default };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var Q=Object.create;var p=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var A=(r,t,e)=>t in r?p(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var H=(r,t)=>{for(var e in t)p(r,e,{get:t[e],enumerable:!0})},I=(r,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of K(t))!z.call(r,i)&&i!==e&&p(r,i,{get:()=>t[i],enumerable:!(n=U(t,i))||n.enumerable});return r};var m=(r,t,e)=>(e=r!=null?Q(j(r)):{},I(t||!r||!r.__esModule?p(e,"default",{value:r,enumerable:!0}):e,r)),$=r=>I(p({},"__esModule",{value:!0}),r);var a=(r,t,e)=>A(r,typeof t!="symbol"?t+"":t,e);var W={};H(W,{default:()=>G});module.exports=$(W);var S=require("@tiptap/core");var T=m(require("@tiptap/extension-document")),x=m(require("@tiptap/extension-paragraph")),P=m(require("@tiptap/extension-text")),C=[T.default,x.default,P.default];var B=m(require("@tiptap/extension-history")),D=m(require("@tiptap/extension-placeholder"));var N=require("@tiptap/core"),_=require("prosemirror-state");function w(r){return r.type.startsWith("image/")}function k(r){return new Promise((t,e)=>{let n=new FileReader;n.onload=()=>t(n.result),n.onerror=e,n.readAsDataURL(r)})}function v(r){return new Promise(t=>{let e=new Image;e.onload=()=>t({width:e.width,height:e.height}),e.src=r})}var F=N.Extension.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let r=async t=>{var i,o,s,l;if(!w(t)){(o=(i=this.options).onFileReject)==null||o.call(i,t);return}let e=await k(t),n=await v(e);(l=(s=this.options).onImageInput)==null||l.call(s,{file:t,base64:e,...n})};return[new _.Plugin({props:{handlePaste:(t,e)=>{var i;let n=(i=e.clipboardData)==null?void 0:i.items;if(!n)return!1;for(let o of n){if(o.kind!=="file")continue;let s=o.getAsFile();if(s)return r(s),!0}return!1},handleDrop:(t,e)=>{var i;let n=(i=e.dataTransfer)==null?void 0:i.files;return!n||n.length===0?!1:(r(n[0]),!0)}}})]}});var R=require("@tiptap/core"),E=require("prosemirror-state"),g=require("prosemirror-view");var f=class{constructor(){a(this,"callbacks",{})}on(t,e){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(e),()=>this.off(t,e)}emit(t,...e){let n=this.callbacks[t];n&&n.forEach(i=>i.apply(this,e))}off(t,e){let n=this.callbacks[t];n&&(e?this.callbacks[t]=n.filter(i=>i!==e):delete this.callbacks[t])}removeAllListeners(){this.callbacks={}}};var d=new E.PluginKey("SuggestionMenuPlugin"),V=(0,R.findParentNode)(r=>r.type.name==="blockContainer"),b=class{constructor(t,e){this.editor=t;a(this,"pluginState");a(this,"state");a(this,"emitUpdate");a(this,"rootEl");a(this,"closeMenu",()=>{this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(d,null))});a(this,"clearQuery",()=>{this.pluginState!==void 0&&this.editor._tiptapEditor.chain().focus().deleteRange({from:this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),to:this.editor._tiptapEditor.state.selection.from}).run()});a(this,"insertMention",t=>{this.editor._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()});this.pluginState=void 0,this.emitUpdate=n=>{var i;if(!this.state)throw new Error("Attempting to update uninitialized suggestions menu");e(n,{...this.state,ignoreQueryLength:(i=this.pluginState)==null?void 0:i.ignoreQueryLength})},setTimeout(()=>{this.rootEl=this.editor._tiptapEditor.view.root})}update(t,e){var c;let n=d.getState(e),i=d.getState(t.state),o=n===void 0&&i!==void 0,s=n!==void 0&&i===void 0;if(!o&&!(n!==void 0&&i!==void 0)&&!s)return;if(this.pluginState=s?n:i,s||!this.editor.isEditable){this.state.show=!1,this.emitUpdate(this.pluginState.triggerCharacter);return}let u=(c=this.rootEl)==null?void 0:c.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);this.editor.isEditable&&u&&(this.state={show:!0,referencePos:u.getBoundingClientRect(),query:this.pluginState.query},this.emitUpdate(this.pluginState.triggerCharacter))}destroy(){}getReplaceRange(){if(!this.pluginState)return null;let t=this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),e=this.editor._tiptapEditor.state.selection.from;return{from:t,to:e}}},y=class extends f{constructor(e){super();a(this,"view");a(this,"plugin");a(this,"triggerCharacters",[]);a(this,"addTriggerCharacter",e=>{this.triggerCharacters.push(e)});a(this,"removeTriggerCharacter",e=>{this.triggerCharacters=this.triggerCharacters.filter(n=>n!==e)});a(this,"closeMenu",()=>this.view.closeMenu());a(this,"clearQuery",()=>this.view.clearQuery());a(this,"insertMention",e=>this.view.insertMention(e));let n=this.triggerCharacters;this.plugin=new E.Plugin({key:d,view:()=>(this.view=new b(e,(i,o)=>{this.emit(`update ${i}`,o)}),this.view),state:{init(){},apply(i,o,s,l){var M;let u=i.getMeta(d);if(typeof u=="object"&&u!==null&&o===void 0)return{triggerCharacter:u.triggerCharacter,deleteTriggerCharacter:u.deleteTriggerCharacter!==!1,queryStartPos:((M=i.selection)==null?void 0:M.from)||l.selection.from,query:"",decorationId:`id_${Math.floor(Math.random()*4294967295)}`,ignoreQueryLength:u==null?void 0:u.ignoreQueryLength};if(o===void 0)return o;if(l.selection.from!==l.selection.to||u===null||i.getMeta("focus")||i.getMeta("blur")||i.getMeta("pointer")||o.triggerCharacter!==void 0&&l.selection.from<o.queryStartPos)return;let c={...o};return c.query=l.doc.textBetween(o.queryStartPos,l.selection.from),c}},props:{handleTextInput(i,o,s,l){let u=this.getState(i.state);return n.includes(l)&&u===void 0?(i.dispatch(i.state.tr.insertText(l).scrollIntoView().setMeta(d,{triggerCharacter:l})),!0):!1},decorations(i){let o=this.getState(i);if(o===void 0)return null;if(!o.deleteTriggerCharacter){let s=V(i.selection);if(s)return g.DecorationSet.create(i.doc,[g.Decoration.node(s.pos,s.pos+s.node.nodeSize,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}return g.DecorationSet.create(i.doc,[g.Decoration.inline(o.queryStartPos-o.triggerCharacter.length,o.queryStartPos,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}}})}onUpdate(e,n){return this.triggerCharacters.includes(e)||this.addTriggerCharacter(e),this.on(`update ${e}`,n)}get shown(){var e,n;return((n=(e=this.view)==null?void 0:e.state)==null?void 0:n.show)||!1}};var q=require("@tiptap/core"),L=q.Node.create({name:"mention",inline:!0,group:"inline",atom:!0,selectable:!1,addAttributes(){return{id:{default:null},label:{default:null}}},parseHTML(){return[{tag:'span[data-type="mention"]'}]},renderHTML({node:r}){return["span",{class:"mention","data-type":"mention","data-id":r.attrs.id,"data-label":r.attrs.label,contenteditable:"false"},r.attrs.label]},renderText({node:r}){return""}});function O(r,t){r._tiptapEditor.chain().insertContent({type:"mention",attrs:{id:t.id,label:t.label}}).run()}var h=class{constructor(t){a(this,"_tiptapEditor");a(this,"suggestionMenus");this.suggestionMenus=new y(this);let e=S.Extension.create({name:"MEditorUIExtension",addProseMirrorPlugins:()=>[this.suggestionMenus.plugin]});this._tiptapEditor=new S.Editor({element:t.element,extensions:[...C,B.default.configure({depth:100,newGroupDelay:500}),L,D.default.configure({placeholder:t.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),F.configure({onImageInput:t.onImageInput,onFileReject:t.onFileReject}),e],onUpdate:({editor:n})=>{t.onChange(n.getText())}})}dispatch(t){this._tiptapEditor.view.dispatch(t)}inserMentionBlock(t){O(this,t)}get isEditable(){return this._tiptapEditor.isEditable===void 0?!0:this._tiptapEditor.isEditable}get domElement(){return this._tiptapEditor.view.dom}clear(){this._tiptapEditor.commands.clearContent(!0),this._tiptapEditor.commands.focus()}};var G=h;
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/editor.ts","../src/schema/base.ts","../src/extensions/ImageInput/ImageInput.ts","../src/utils/file.ts","../src/extensions/SuggestionMenu/SuggestionPlugin.ts","../src/utils/EventEmitter.ts","../src/blocks/MentionBlock.ts","../src/api/InsertMentionBlock.ts"],"sourcesContent":["import MEditor from \"./editor\";\n\nexport default MEditor\n\nexport * from \"./extensions/SuggestionMenu/types\"\nexport * from \"./extensions/ImageInput/types\"","import { Editor, Extension } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport History from '@tiptap/extension-history'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { ImageInputPayload } from './extensions/ImageInput/types'\nimport { ImageInputExtension } from './extensions/ImageInput/ImageInput'\nimport { Transaction } from 'prosemirror-state'\nimport { SuggestionMenuProseMirrorPlugin } from './extensions/SuggestionMenu/SuggestionPlugin'\nimport { MentionBlock } from './blocks/MentionBlock'\nimport { type MentionItem } from './extensions/SuggestionMenu/types'\nimport { InsertMentionBlock } from './api/InsertMentionBlock'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n onChange: (text: string) => void\n placeholder?: string\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\n\nexport default class MEditor {\n _tiptapEditor: Editor\n public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin\n\n constructor(options: MentionEditorOptions) {\n this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this)\n const MEditorUIExtension = Extension.create({\n name: 'MEditorUIExtension',\n addProseMirrorPlugins: () => {\n return [\n this.suggestionMenus.plugin,\n ]\n },\n })\n\n this._tiptapEditor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n History.configure({\n depth: 100,\n newGroupDelay: 500,\n }),\n MentionBlock,\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n MEditorUIExtension,\n ],\n onUpdate: ({ editor }) => {\n options.onChange(editor.getText())\n }\n })\n }\n\n\n dispatch(tr: Transaction) {\n this._tiptapEditor.view.dispatch(tr)\n }\n\n public inserMentionBlock(item: MentionItem) {\n InsertMentionBlock(this, item)\n // this._tiptapEditor\n // .chain()\n // .focus()\n // .insertContent({\n // type: 'mention',\n // attrs: item\n // })\n // .insertContent(' ')\n // .run()\n }\n\n public get isEditable(): boolean {\n return this._tiptapEditor.isEditable === undefined ? true : this._tiptapEditor.isEditable\n }\n\n public get domElement() {\n return this._tiptapEditor.view.dom as HTMLDivElement\n }\n\n public clear() {\n this._tiptapEditor.commands.clearContent(true)\n this._tiptapEditor.commands.focus()\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n} from '../../utils/file'\nimport { ImageInputPayload, ImageInputOptions } from './types'\n\n\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n","import { findParentNode } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport MEditor from '../../editor'\nimport { EventEmitter } from '../../utils/EventEmitter'\nimport { SuggestionMenuState, MentionItem } from './types'\n\ntype SuggestionPluginState =\n | {\n triggerCharacter: string\n deleteTriggerCharacter: boolean\n queryStartPos: number\n query: string\n decorationId: string\n ignoreQueryLength?: boolean\n }\n | undefined\n\nexport const suggestionMenuPluginKey = new PluginKey('SuggestionMenuPlugin')\nconst findBlock = findParentNode(node => node.type.name === 'blockContainer')\n\nclass SuggestionMenuView {\n pluginState: SuggestionPluginState\n public state?: SuggestionMenuState\n public emitUpdate: (triggerCharacter: string) => void\n private rootEl?: Document | ShadowRoot\n\n constructor(\n private readonly editor: MEditor,\n emitUpdate: (menuName: string, state: SuggestionMenuState) => void\n ) {\n this.pluginState = undefined\n\n this.emitUpdate = (menuName: string) => {\n if (!this.state) {\n throw new Error('Attempting to update uninitialized suggestions menu')\n }\n emitUpdate(menuName, {\n ...this.state,\n ignoreQueryLength: this.pluginState?.ignoreQueryLength,\n })\n }\n setTimeout(() => {\n this.rootEl = this.editor._tiptapEditor.view.root\n })\n\n }\n\n update(view: EditorView, prevState: EditorState) {\n\n const prev: SuggestionPluginState = suggestionMenuPluginKey.getState(prevState)\n const next: SuggestionPluginState = suggestionMenuPluginKey.getState(view.state)\n\n \n const started = prev === undefined && next !== undefined\n const stopped = prev !== undefined && next === undefined\n const changed = prev !== undefined && next !== undefined\n\n \n if (!started && !changed && !stopped) {\n return\n }\n\n this.pluginState = stopped ? prev : next\n\n if (stopped || !this.editor.isEditable) {\n\n this.state!.show = false\n this.emitUpdate(this.pluginState!.triggerCharacter)\n\n return\n }\n\n const decorationNode = this.rootEl?.querySelector(`[data-decoration-id=\"${this.pluginState!.decorationId}\"]`)\n\n if (this.editor.isEditable && decorationNode) {\n\n this.state = {\n show: true,\n referencePos: decorationNode.getBoundingClientRect(),\n query: this.pluginState!.query,\n }\n\n this.emitUpdate(this.pluginState!.triggerCharacter!)\n }\n }\n\n destroy() {\n\n }\n\n closeMenu = () => {\n this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(suggestionMenuPluginKey, null))\n }\n\n clearQuery = () => {\n if (this.pluginState === undefined) {\n return\n }\n\n this.editor._tiptapEditor\n .chain()\n .focus()\n .deleteRange({\n from:\n this.pluginState.queryStartPos! -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter!.length : 0),\n to: this.editor._tiptapEditor.state.selection.from,\n })\n .run()\n }\n\n insertMention = (item: MentionItem) => {\n this.editor._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n getReplaceRange() {\n if (!this.pluginState) return null\n\n const from = this.pluginState.queryStartPos -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0)\n\n const to = this.editor._tiptapEditor.state.selection.from\n\n return { from, to }\n }\n\n}\n\nexport class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {\n public view: SuggestionMenuView\n public readonly plugin: Plugin\n private triggerCharacters: string[] = []\n\n constructor(editor: MEditor) {\n super()\n const triggerCharacters = this.triggerCharacters\n\n this.plugin = new Plugin({\n key: suggestionMenuPluginKey,\n view: () => {\n this.view = new SuggestionMenuView(editor, (triggerCharacter, state) => {\n this.emit(`update ${triggerCharacter}`, state)\n })\n return this.view\n },\n state: {\n init(): SuggestionPluginState {\n return undefined\n },\n apply(transaction, prev, _oldState, newState): SuggestionPluginState {\n const suggestionPluginTransactionMeta: {\n triggerCharacter: string\n deleteTriggerCharacter?: boolean\n ignoreQueryLength?: boolean\n } | null = transaction.getMeta(suggestionMenuPluginKey)\n\n\n if (\n typeof suggestionPluginTransactionMeta === 'object' &&\n suggestionPluginTransactionMeta !== null &&\n prev === undefined\n ) {\n return {\n triggerCharacter: suggestionPluginTransactionMeta.triggerCharacter,\n deleteTriggerCharacter: suggestionPluginTransactionMeta.deleteTriggerCharacter !== false,\n queryStartPos: transaction.selection?.from || newState.selection.from,\n query: '',\n decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,\n ignoreQueryLength: suggestionPluginTransactionMeta?.ignoreQueryLength,\n }\n }\n\n\n if (prev === undefined) {\n return prev\n }\n\n\n if (\n newState.selection.from !== newState.selection.to ||\n suggestionPluginTransactionMeta === null ||\n transaction.getMeta('focus') ||\n transaction.getMeta('blur') ||\n transaction.getMeta('pointer') ||\n (prev.triggerCharacter !== undefined && newState.selection.from < prev.queryStartPos!)\n ) {\n\n return undefined\n }\n\n const next = { ...prev }\n\n\n next.query = newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)\n\n return next\n }\n },\n\n props: {\n // 当用户输入一个 trigger 字符,且当前没有 suggestion 激活时:\n // 插件拦截这次输入,自己插入字符,并通过 transaction meta 通知 state 打开 suggestion 菜单;\n // 其余情况下,完全不干预编辑器行为。\n handleTextInput(view, _from, _to, text) {\n\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(view.state)\n\n if (triggerCharacters.includes(text) && suggestionPluginState === undefined) {\n view.dispatch(\n view.state.tr.insertText(text).scrollIntoView().setMeta(suggestionMenuPluginKey, {\n triggerCharacter: text,\n })\n )\n\n return true\n }\n return false\n },\n\n decorations(state) {\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(state)\n\n if (suggestionPluginState === undefined) {\n return null\n }\n\n \n if (!suggestionPluginState.deleteTriggerCharacter) {\n const blockNode = findBlock(state.selection)\n if (blockNode) {\n return DecorationSet.create(state.doc, [\n Decoration.node(blockNode.pos, blockNode.pos + blockNode.node.nodeSize, {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }),\n ])\n }\n }\n \n return DecorationSet.create(state.doc, [\n Decoration.inline(\n suggestionPluginState.queryStartPos! - suggestionPluginState.triggerCharacter!.length,\n suggestionPluginState.queryStartPos!,\n {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }\n ),\n ])\n },\n }\n })\n }\n\n public onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void) {\n if (!this.triggerCharacters.includes(triggerCharacter)) {\n this.addTriggerCharacter(triggerCharacter)\n }\n \n return this.on(`update ${triggerCharacter}`, callback)\n }\n\n addTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters.push(triggerCharacter)\n }\n\n \n removeTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters = this.triggerCharacters.filter(c => c !== triggerCharacter)\n }\n\n closeMenu = () => this.view!.closeMenu()\n\n clearQuery = () => this.view!.clearQuery()\n\n insertMention = (item: MentionItem) => this.view!.insertMention(item)\n\n public get shown() {\n return this.view?.state?.show || false\n }\n\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n private callbacks: { [key: string]: Function[] } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>) {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return () => this.off(event, fn)\n }\n\n protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n }\n\n protected removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { Node } from '@tiptap/core'\n\nexport const MentionBlock = Node.create({\n name: 'mention',\n inline: true,\n group: 'inline',\n atom: true,\n selectable: false,\n\n addAttributes() {\n return {\n id: { default: null },\n label: { default: null },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'span[data-type=\"mention\"]',\n },\n ]\n },\n\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-type': 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n contenteditable: 'false', // 非编辑\n },\n node.attrs.label,\n ]\n },\n\n // 新增:确保mention节点可以正确序列化/反序列化\n renderText({ node }) {\n return ''\n },\n})\n\n\n","import { Selection } from 'prosemirror-state'\nimport MEditor from '../editor'\n\nexport function InsertMentionBlock(\n editor: MEditor,\n item: { id: string; label: string }\n) {\n\n editor._tiptapEditor.chain().insertContent({\n type: 'mention',\n attrs: {\n id: item.id,\n label: item.label,\n }\n }).run() \n\n}\n\n"],"mappings":"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","names":["index_exports","__export","index_default","__toCommonJS","import_core","import_extension_document","import_extension_paragraph","import_extension_text","baseExtensions","Document","Paragraph","Text","import_extension_history","import_extension_placeholder","import_core","import_prosemirror_state","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","handleFile","file","_a","_b","_c","_d","isImageFile","base64","fileToBase64","size","getImageSize","_","event","items","item","files","import_core","import_prosemirror_state","import_prosemirror_view","EventEmitter","__publicField","event","fn","args","callbacks","callback","suggestionMenuPluginKey","findBlock","node","SuggestionMenuView","editor","emitUpdate","__publicField","item","menuName","_a","view","prevState","prev","next","started","stopped","decorationNode","from","to","SuggestionMenuProseMirrorPlugin","EventEmitter","triggerCharacter","c","triggerCharacters","state","transaction","_oldState","newState","suggestionPluginTransactionMeta","_from","_to","text","suggestionPluginState","blockNode","callback","_b","import_core","MentionBlock","node","InsertMentionBlock","editor","item","MEditor","options","__publicField","SuggestionMenuProseMirrorPlugin","MEditorUIExtension","baseExtensions","History","MentionBlock","Placeholder","ImageInputExtension","editor","tr","item","InsertMentionBlock","index_default","MEditor"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var C=Object.defineProperty;var w=(n,t,e)=>t in n?C(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var a=(n,t,e)=>w(n,typeof t!="symbol"?t+"":t,e);import{Editor as D,Extension as Q}from"@tiptap/core";import k from"@tiptap/extension-document";import v from"@tiptap/extension-paragraph";import N from"@tiptap/extension-text";var y=[k,v,N];import U from"@tiptap/extension-history";import K from"@tiptap/extension-placeholder";import{Extension as _}from"@tiptap/core";import{Plugin as F}from"prosemirror-state";function E(n){return n.type.startsWith("image/")}function S(n){return new Promise((t,e)=>{let i=new FileReader;i.onload=()=>t(i.result),i.onerror=e,i.readAsDataURL(n)})}function b(n){return new Promise(t=>{let e=new Image;e.onload=()=>t({width:e.width,height:e.height}),e.src=n})}var M=_.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let n=async t=>{var r,o,s,l;if(!E(t)){(o=(r=this.options).onFileReject)==null||o.call(r,t);return}let e=await S(t),i=await b(e);(l=(s=this.options).onImageInput)==null||l.call(s,{file:t,base64:e,...i})};return[new F({props:{handlePaste:(t,e)=>{var r;let i=(r=e.clipboardData)==null?void 0:r.items;if(!i)return!1;for(let o of i){if(o.kind!=="file")continue;let s=o.getAsFile();if(s)return n(s),!0}return!1},handleDrop:(t,e)=>{var r;let i=(r=e.dataTransfer)==null?void 0:r.files;return!i||i.length===0?!1:(n(i[0]),!0)}}})]}});import{findParentNode as R}from"@tiptap/core";import{Plugin as q,PluginKey as L}from"prosemirror-state";import{Decoration as I,DecorationSet as T}from"prosemirror-view";var p=class{constructor(){a(this,"callbacks",{})}on(t,e){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(e),()=>this.off(t,e)}emit(t,...e){let i=this.callbacks[t];i&&i.forEach(r=>r.apply(this,e))}off(t,e){let i=this.callbacks[t];i&&(e?this.callbacks[t]=i.filter(r=>r!==e):delete this.callbacks[t])}removeAllListeners(){this.callbacks={}}};var d=new L("SuggestionMenuPlugin"),O=R(n=>n.type.name==="blockContainer"),h=class{constructor(t,e){this.editor=t;a(this,"pluginState");a(this,"state");a(this,"emitUpdate");a(this,"rootEl");a(this,"closeMenu",()=>{this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(d,null))});a(this,"clearQuery",()=>{this.pluginState!==void 0&&this.editor._tiptapEditor.chain().focus().deleteRange({from:this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),to:this.editor._tiptapEditor.state.selection.from}).run()});a(this,"insertMention",t=>{this.editor._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()});this.pluginState=void 0,this.emitUpdate=i=>{var r;if(!this.state)throw new Error("Attempting to update uninitialized suggestions menu");e(i,{...this.state,ignoreQueryLength:(r=this.pluginState)==null?void 0:r.ignoreQueryLength})},setTimeout(()=>{this.rootEl=this.editor._tiptapEditor.view.root})}update(t,e){var g;let i=d.getState(e),r=d.getState(t.state),o=i===void 0&&r!==void 0,s=i!==void 0&&r===void 0;if(!o&&!(i!==void 0&&r!==void 0)&&!s)return;if(this.pluginState=s?i:r,s||!this.editor.isEditable){this.state.show=!1,this.emitUpdate(this.pluginState.triggerCharacter);return}let u=(g=this.rootEl)==null?void 0:g.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);this.editor.isEditable&&u&&(this.state={show:!0,referencePos:u.getBoundingClientRect(),query:this.pluginState.query},this.emitUpdate(this.pluginState.triggerCharacter))}destroy(){}getReplaceRange(){if(!this.pluginState)return null;let t=this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),e=this.editor._tiptapEditor.state.selection.from;return{from:t,to:e}}},m=class extends p{constructor(e){super();a(this,"view");a(this,"plugin");a(this,"triggerCharacters",[]);a(this,"addTriggerCharacter",e=>{this.triggerCharacters.push(e)});a(this,"removeTriggerCharacter",e=>{this.triggerCharacters=this.triggerCharacters.filter(i=>i!==e)});a(this,"closeMenu",()=>this.view.closeMenu());a(this,"clearQuery",()=>this.view.clearQuery());a(this,"insertMention",e=>this.view.insertMention(e));let i=this.triggerCharacters;this.plugin=new q({key:d,view:()=>(this.view=new h(e,(r,o)=>{this.emit(`update ${r}`,o)}),this.view),state:{init(){},apply(r,o,s,l){var f;let u=r.getMeta(d);if(typeof u=="object"&&u!==null&&o===void 0)return{triggerCharacter:u.triggerCharacter,deleteTriggerCharacter:u.deleteTriggerCharacter!==!1,queryStartPos:((f=r.selection)==null?void 0:f.from)||l.selection.from,query:"",decorationId:`id_${Math.floor(Math.random()*4294967295)}`,ignoreQueryLength:u==null?void 0:u.ignoreQueryLength};if(o===void 0)return o;if(l.selection.from!==l.selection.to||u===null||r.getMeta("focus")||r.getMeta("blur")||r.getMeta("pointer")||o.triggerCharacter!==void 0&&l.selection.from<o.queryStartPos)return;let g={...o};return g.query=l.doc.textBetween(o.queryStartPos,l.selection.from),g}},props:{handleTextInput(r,o,s,l){let u=this.getState(r.state);return i.includes(l)&&u===void 0?(r.dispatch(r.state.tr.insertText(l).scrollIntoView().setMeta(d,{triggerCharacter:l})),!0):!1},decorations(r){let o=this.getState(r);if(o===void 0)return null;if(!o.deleteTriggerCharacter){let s=O(r.selection);if(s)return T.create(r.doc,[I.node(s.pos,s.pos+s.node.nodeSize,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}return T.create(r.doc,[I.inline(o.queryStartPos-o.triggerCharacter.length,o.queryStartPos,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}}})}onUpdate(e,i){return this.triggerCharacters.includes(e)||this.addTriggerCharacter(e),this.on(`update ${e}`,i)}get shown(){var e,i;return((i=(e=this.view)==null?void 0:e.state)==null?void 0:i.show)||!1}};import{Node as B}from"@tiptap/core";var x=B.create({name:"mention",inline:!0,group:"inline",atom:!0,selectable:!1,addAttributes(){return{id:{default:null},label:{default:null}}},parseHTML(){return[{tag:'span[data-type="mention"]'}]},renderHTML({node:n}){return["span",{class:"mention","data-type":"mention","data-id":n.attrs.id,"data-label":n.attrs.label,contenteditable:"false"},n.attrs.label]},renderText({node:n}){return""}});function P(n,t){n._tiptapEditor.chain().insertContent({type:"mention",attrs:{id:t.id,label:t.label}}).run()}var c=class{constructor(t){a(this,"_tiptapEditor");a(this,"suggestionMenus");this.suggestionMenus=new m(this);let e=Q.create({name:"MEditorUIExtension",addProseMirrorPlugins:()=>[this.suggestionMenus.plugin]});this._tiptapEditor=new D({element:t.element,extensions:[...y,U.configure({depth:100,newGroupDelay:500}),x,K.configure({placeholder:t.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),M.configure({onImageInput:t.onImageInput,onFileReject:t.onFileReject}),e],onUpdate:({editor:i})=>{t.onChange(i.getText())}})}dispatch(t){this._tiptapEditor.view.dispatch(t)}inserMentionBlock(t){P(this,t)}get isEditable(){return this._tiptapEditor.isEditable===void 0?!0:this._tiptapEditor.isEditable}get domElement(){return this._tiptapEditor.view.dom}clear(){this._tiptapEditor.commands.clearContent(!0),this._tiptapEditor.commands.focus()}};var It=c;export{It as default};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor.ts","../src/schema/base.ts","../src/extensions/ImageInput/ImageInput.ts","../src/utils/file.ts","../src/extensions/SuggestionMenu/SuggestionPlugin.ts","../src/utils/EventEmitter.ts","../src/blocks/MentionBlock.ts","../src/api/InsertMentionBlock.ts","../src/index.ts"],"sourcesContent":["import { Editor, Extension } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport History from '@tiptap/extension-history'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { ImageInputPayload } from './extensions/ImageInput/types'\nimport { ImageInputExtension } from './extensions/ImageInput/ImageInput'\nimport { Transaction } from 'prosemirror-state'\nimport { SuggestionMenuProseMirrorPlugin } from './extensions/SuggestionMenu/SuggestionPlugin'\nimport { MentionBlock } from './blocks/MentionBlock'\nimport { type MentionItem } from './extensions/SuggestionMenu/types'\nimport { InsertMentionBlock } from './api/InsertMentionBlock'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n onChange: (text: string) => void\n placeholder?: string\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\n\nexport default class MEditor {\n _tiptapEditor: Editor\n public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin\n\n constructor(options: MentionEditorOptions) {\n this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this)\n const MEditorUIExtension = Extension.create({\n name: 'MEditorUIExtension',\n addProseMirrorPlugins: () => {\n return [\n this.suggestionMenus.plugin,\n ]\n },\n })\n\n this._tiptapEditor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n History.configure({\n depth: 100,\n newGroupDelay: 500,\n }),\n MentionBlock,\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n MEditorUIExtension,\n ],\n onUpdate: ({ editor }) => {\n options.onChange(editor.getText())\n }\n })\n }\n\n\n dispatch(tr: Transaction) {\n this._tiptapEditor.view.dispatch(tr)\n }\n\n public inserMentionBlock(item: MentionItem) {\n InsertMentionBlock(this, item)\n // this._tiptapEditor\n // .chain()\n // .focus()\n // .insertContent({\n // type: 'mention',\n // attrs: item\n // })\n // .insertContent(' ')\n // .run()\n }\n\n public get isEditable(): boolean {\n return this._tiptapEditor.isEditable === undefined ? true : this._tiptapEditor.isEditable\n }\n\n public get domElement() {\n return this._tiptapEditor.view.dom as HTMLDivElement\n }\n\n public clear() {\n this._tiptapEditor.commands.clearContent(true)\n this._tiptapEditor.commands.focus()\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n} from '../../utils/file'\nimport { ImageInputPayload, ImageInputOptions } from './types'\n\n\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n","import { findParentNode } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport MEditor from '../../editor'\nimport { EventEmitter } from '../../utils/EventEmitter'\nimport { SuggestionMenuState, MentionItem } from './types'\n\ntype SuggestionPluginState =\n | {\n triggerCharacter: string\n deleteTriggerCharacter: boolean\n queryStartPos: number\n query: string\n decorationId: string\n ignoreQueryLength?: boolean\n }\n | undefined\n\nexport const suggestionMenuPluginKey = new PluginKey('SuggestionMenuPlugin')\nconst findBlock = findParentNode(node => node.type.name === 'blockContainer')\n\nclass SuggestionMenuView {\n pluginState: SuggestionPluginState\n public state?: SuggestionMenuState\n public emitUpdate: (triggerCharacter: string) => void\n private rootEl?: Document | ShadowRoot\n\n constructor(\n private readonly editor: MEditor,\n emitUpdate: (menuName: string, state: SuggestionMenuState) => void\n ) {\n this.pluginState = undefined\n\n this.emitUpdate = (menuName: string) => {\n if (!this.state) {\n throw new Error('Attempting to update uninitialized suggestions menu')\n }\n emitUpdate(menuName, {\n ...this.state,\n ignoreQueryLength: this.pluginState?.ignoreQueryLength,\n })\n }\n setTimeout(() => {\n this.rootEl = this.editor._tiptapEditor.view.root\n })\n\n }\n\n update(view: EditorView, prevState: EditorState) {\n\n const prev: SuggestionPluginState = suggestionMenuPluginKey.getState(prevState)\n const next: SuggestionPluginState = suggestionMenuPluginKey.getState(view.state)\n\n \n const started = prev === undefined && next !== undefined\n const stopped = prev !== undefined && next === undefined\n const changed = prev !== undefined && next !== undefined\n\n \n if (!started && !changed && !stopped) {\n return\n }\n\n this.pluginState = stopped ? prev : next\n\n if (stopped || !this.editor.isEditable) {\n\n this.state!.show = false\n this.emitUpdate(this.pluginState!.triggerCharacter)\n\n return\n }\n\n const decorationNode = this.rootEl?.querySelector(`[data-decoration-id=\"${this.pluginState!.decorationId}\"]`)\n\n if (this.editor.isEditable && decorationNode) {\n\n this.state = {\n show: true,\n referencePos: decorationNode.getBoundingClientRect(),\n query: this.pluginState!.query,\n }\n\n this.emitUpdate(this.pluginState!.triggerCharacter!)\n }\n }\n\n destroy() {\n\n }\n\n closeMenu = () => {\n this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(suggestionMenuPluginKey, null))\n }\n\n clearQuery = () => {\n if (this.pluginState === undefined) {\n return\n }\n\n this.editor._tiptapEditor\n .chain()\n .focus()\n .deleteRange({\n from:\n this.pluginState.queryStartPos! -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter!.length : 0),\n to: this.editor._tiptapEditor.state.selection.from,\n })\n .run()\n }\n\n insertMention = (item: MentionItem) => {\n this.editor._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n getReplaceRange() {\n if (!this.pluginState) return null\n\n const from = this.pluginState.queryStartPos -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0)\n\n const to = this.editor._tiptapEditor.state.selection.from\n\n return { from, to }\n }\n\n}\n\nexport class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {\n public view: SuggestionMenuView\n public readonly plugin: Plugin\n private triggerCharacters: string[] = []\n\n constructor(editor: MEditor) {\n super()\n const triggerCharacters = this.triggerCharacters\n\n this.plugin = new Plugin({\n key: suggestionMenuPluginKey,\n view: () => {\n this.view = new SuggestionMenuView(editor, (triggerCharacter, state) => {\n this.emit(`update ${triggerCharacter}`, state)\n })\n return this.view\n },\n state: {\n init(): SuggestionPluginState {\n return undefined\n },\n apply(transaction, prev, _oldState, newState): SuggestionPluginState {\n const suggestionPluginTransactionMeta: {\n triggerCharacter: string\n deleteTriggerCharacter?: boolean\n ignoreQueryLength?: boolean\n } | null = transaction.getMeta(suggestionMenuPluginKey)\n\n\n if (\n typeof suggestionPluginTransactionMeta === 'object' &&\n suggestionPluginTransactionMeta !== null &&\n prev === undefined\n ) {\n return {\n triggerCharacter: suggestionPluginTransactionMeta.triggerCharacter,\n deleteTriggerCharacter: suggestionPluginTransactionMeta.deleteTriggerCharacter !== false,\n queryStartPos: transaction.selection?.from || newState.selection.from,\n query: '',\n decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,\n ignoreQueryLength: suggestionPluginTransactionMeta?.ignoreQueryLength,\n }\n }\n\n\n if (prev === undefined) {\n return prev\n }\n\n\n if (\n newState.selection.from !== newState.selection.to ||\n suggestionPluginTransactionMeta === null ||\n transaction.getMeta('focus') ||\n transaction.getMeta('blur') ||\n transaction.getMeta('pointer') ||\n (prev.triggerCharacter !== undefined && newState.selection.from < prev.queryStartPos!)\n ) {\n\n return undefined\n }\n\n const next = { ...prev }\n\n\n next.query = newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)\n\n return next\n }\n },\n\n props: {\n // 当用户输入一个 trigger 字符,且当前没有 suggestion 激活时:\n // 插件拦截这次输入,自己插入字符,并通过 transaction meta 通知 state 打开 suggestion 菜单;\n // 其余情况下,完全不干预编辑器行为。\n handleTextInput(view, _from, _to, text) {\n\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(view.state)\n\n if (triggerCharacters.includes(text) && suggestionPluginState === undefined) {\n view.dispatch(\n view.state.tr.insertText(text).scrollIntoView().setMeta(suggestionMenuPluginKey, {\n triggerCharacter: text,\n })\n )\n\n return true\n }\n return false\n },\n\n decorations(state) {\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(state)\n\n if (suggestionPluginState === undefined) {\n return null\n }\n\n \n if (!suggestionPluginState.deleteTriggerCharacter) {\n const blockNode = findBlock(state.selection)\n if (blockNode) {\n return DecorationSet.create(state.doc, [\n Decoration.node(blockNode.pos, blockNode.pos + blockNode.node.nodeSize, {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }),\n ])\n }\n }\n \n return DecorationSet.create(state.doc, [\n Decoration.inline(\n suggestionPluginState.queryStartPos! - suggestionPluginState.triggerCharacter!.length,\n suggestionPluginState.queryStartPos!,\n {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }\n ),\n ])\n },\n }\n })\n }\n\n public onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void) {\n if (!this.triggerCharacters.includes(triggerCharacter)) {\n this.addTriggerCharacter(triggerCharacter)\n }\n \n return this.on(`update ${triggerCharacter}`, callback)\n }\n\n addTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters.push(triggerCharacter)\n }\n\n \n removeTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters = this.triggerCharacters.filter(c => c !== triggerCharacter)\n }\n\n closeMenu = () => this.view!.closeMenu()\n\n clearQuery = () => this.view!.clearQuery()\n\n insertMention = (item: MentionItem) => this.view!.insertMention(item)\n\n public get shown() {\n return this.view?.state?.show || false\n }\n\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n private callbacks: { [key: string]: Function[] } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>) {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return () => this.off(event, fn)\n }\n\n protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n }\n\n protected removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { Node } from '@tiptap/core'\n\nexport const MentionBlock = Node.create({\n name: 'mention',\n inline: true,\n group: 'inline',\n atom: true,\n selectable: false,\n\n addAttributes() {\n return {\n id: { default: null },\n label: { default: null },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'span[data-type=\"mention\"]',\n },\n ]\n },\n\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-type': 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n contenteditable: 'false', // 非编辑\n },\n node.attrs.label,\n ]\n },\n\n // 新增:确保mention节点可以正确序列化/反序列化\n renderText({ node }) {\n return ''\n },\n})\n\n\n","import { Selection } from 'prosemirror-state'\nimport MEditor from '../editor'\n\nexport function InsertMentionBlock(\n editor: MEditor,\n item: { id: string; label: string }\n) {\n\n editor._tiptapEditor.chain().insertContent({\n type: 'mention',\n attrs: {\n id: item.id,\n label: item.label,\n }\n }).run() \n\n}\n\n","import MEditor from \"./editor\";\n\nexport default MEditor\n\nexport * from \"./extensions/SuggestionMenu/types\"\nexport * from \"./extensions/ImageInput/types\""],"mappings":"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","names":["Editor","Extension","Document","Paragraph","Text","baseExtensions","History","Placeholder","Extension","Plugin","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","Extension","handleFile","file","_a","_b","_c","_d","isImageFile","base64","fileToBase64","size","getImageSize","Plugin","_","event","items","item","files","findParentNode","Plugin","PluginKey","Decoration","DecorationSet","EventEmitter","__publicField","event","fn","args","callbacks","callback","suggestionMenuPluginKey","PluginKey","findBlock","findParentNode","node","SuggestionMenuView","editor","emitUpdate","__publicField","item","menuName","_a","view","prevState","prev","next","started","stopped","decorationNode","from","to","SuggestionMenuProseMirrorPlugin","EventEmitter","triggerCharacter","c","triggerCharacters","Plugin","state","transaction","_oldState","newState","suggestionPluginTransactionMeta","_from","_to","text","suggestionPluginState","blockNode","DecorationSet","Decoration","callback","_b","Node","MentionBlock","node","InsertMentionBlock","editor","item","MEditor","options","__publicField","SuggestionMenuProseMirrorPlugin","MEditorUIExtension","Extension","Editor","baseExtensions","History","MentionBlock","Placeholder","ImageInputExtension","editor","tr","item","InsertMentionBlock","index_default","MEditor"]}
package/package.json CHANGED
@@ -1,39 +1,40 @@
1
1
  {
2
- "name": "@shenjipo/mention-editor",
3
- "version": "1.0.0",
4
- "module": "dist/esm/index.mjs",
5
- "exports": {
6
- ".": {
7
- "import": "./dist/esm/index.mjs",
8
- "types": "./dist/esm/index.d.mts"
9
- }
10
- },
11
- "files": [
12
- "dist"
13
- ],
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "keywords": [],
18
- "author": "",
19
- "license": "ISC",
20
- "dependencies": {
21
- "@tiptap/core": "2.7.1",
22
- "@tiptap/extension-document": "2.7.1",
23
- "@tiptap/extension-text": "2.7.1",
24
- "@tiptap/extension-paragraph": "2.7.1",
25
- "@tiptap/extension-mention": "2.7.1",
26
- "@tiptap/extension-placeholder": "2.7.1",
27
- "@tiptap/extension-hard-break": "2.7.1",
28
- "prosemirror-state": "^1.4.3",
29
- "prosemirror-model": "^1.21.0",
30
- "prosemirror-view": "^1.33.7"
31
- },
32
- "main": "index.js",
33
- "devDependencies": {},
34
- "description": "",
35
- "scripts": {
36
- "build:watch": "tsup --watch",
37
- "build": "tsup"
38
- }
2
+ "name": "@shenjipo/mention-editor",
3
+ "version": "2.1.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.mjs",
10
+ "types": "./dist/index.d.mts"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build:watch": "tsup --watch",
21
+ "build": "tsup"
22
+ },
23
+ "keywords": [],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "packageManager": "pnpm@10.10.0",
27
+ "dependencies": {
28
+ "@tiptap/core": "2.7.1",
29
+ "@tiptap/extension-document": "2.7.1",
30
+ "@tiptap/extension-text": "2.7.1",
31
+ "@tiptap/extension-paragraph": "2.7.1",
32
+ "@tiptap/extension-placeholder": "2.7.1",
33
+ "@tiptap/extension-history": "2.7.1",
34
+ "prosemirror-state": "^1.4.3",
35
+ "prosemirror-model": "^1.21.0",
36
+ "prosemirror-view": "^1.33.7"
37
+ },
38
+ "devDependencies": {},
39
+ "description": ""
39
40
  }
@@ -1,46 +0,0 @@
1
- import { Editor } from '@tiptap/core';
2
-
3
- interface MentionBridge {
4
- query: string;
5
- setQuery: (val: string) => void;
6
- items: MentionItem[];
7
- selectedIndex: number;
8
- confirm: (index: number) => void;
9
- setOptions: (val: Array<MentionItem>) => void;
10
- }
11
- interface MentionItem {
12
- id: string;
13
- label: string;
14
- description?: string;
15
- disabled?: boolean;
16
- }
17
- interface MentionStorage {
18
- active: boolean;
19
- }
20
-
21
- interface ImageInputPayload {
22
- file: File;
23
- base64: string;
24
- width?: number;
25
- height?: number;
26
- }
27
-
28
- interface MentionEditorOptions {
29
- element: HTMLElement;
30
- suggestionDom: HTMLElement;
31
- bridge: MentionBridge;
32
- onUpdate: (text: string) => void;
33
- content?: string;
34
- onCommand?: (item: MentionItem) => void;
35
- placeholder?: string;
36
- onImageInput?: (payload: ImageInputPayload) => void;
37
- onFileReject?: (file: File) => void;
38
- }
39
- interface MentionEditor {
40
- editor: Editor;
41
- getHtml: () => string;
42
- destroy: () => void;
43
- }
44
- declare function createMentionEditor(options: MentionEditorOptions): MentionEditor;
45
-
46
- export { type MentionBridge, type MentionEditor, type MentionItem, type MentionStorage, createMentionEditor };
@@ -1,2 +0,0 @@
1
- import{Editor as B}from"@tiptap/core";import E from"@tiptap/extension-document";import h from"@tiptap/extension-paragraph";import D from"@tiptap/extension-text";var u=[E,h,D];import P from"@tiptap/extension-mention";function l(e){return()=>{let t=null,r=null,n=[],i=0,m,d=null;function x(){a(),document.body.appendChild(e.suggestionDom),F(e.suggestionDom,t)}let a=()=>{e.bridge.selectedIndex=i};e.bridge.confirm=o=>{g(o)};function g(o){let s=n[o];s&&(s.disabled?e.suggestionDom.remove():(r&&(d.chain().focus().deleteRange(r).run(),r=null),m(s)))}return{onStart(o){d=o.editor,r=o.range,t=o.clientRect(),n=e.bridge.items,m=o.command,i=0,x()},onUpdate(o){n=e.bridge.items,r=o.range},onKeyDown({event:o}){return o.key==="ArrowDown"?(i=(i+1)%n.length,a(),!0):o.key==="ArrowUp"?(i=(i-1+n.length)%n.length,a(),!0):o.key==="Enter"?(g(i),!0):!1},onExit(){e.suggestionDom.remove()}}}}function F(e,t){e.style.position="absolute",e.style.left=`${t.left}px`,e.style.top=`${t.bottom+4}px`}function c(e){return{char:"/",startOfLine:!1,items:({query:t})=>(e.bridge.setQuery(t),e.bridge.items),command:({editor:t,range:r,props:n})=>{t.chain().focus().insertContent({type:"mention",attrs:{id:n.id,label:n.label}}).run(),e.onCommand?.(n)},render:l({suggestionDom:e.suggestionDom,bridge:e.bridge})}}function f(e){return P.configure({HTMLAttributes:{class:"mention","data-type":"mention"},suggestion:c(e),renderHTML({node:t}){return["span",{class:"mention","data-id":t.attrs.id,"data-label":t.attrs.label},t.attrs.label]}})}import H from"@tiptap/extension-placeholder";import{Extension as w}from"@tiptap/core";var p=w.create({name:"customKeyboard",addKeyboardShortcuts(){return{"Shift-Enter":({editor:e})=>e.commands.setHardBreak(),Enter:({editor:e})=>(e.emit("enter"),!1)}}});import v from"@tiptap/extension-hard-break";import{Extension as R}from"@tiptap/core";import{Plugin as T}from"prosemirror-state";function I(e){return e.type.startsWith("image/")}function M(e){return new Promise((t,r)=>{let n=new FileReader;n.onload=()=>t(n.result),n.onerror=r,n.readAsDataURL(e)})}function b(e){return new Promise(t=>{let r=new Image;r.onload=()=>t({width:r.width,height:r.height}),r.src=e})}var y=R.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let e=async t=>{if(!I(t)){this.options.onFileReject?.(t);return}let r=await M(t),n=await b(r);this.options.onImageInput?.({file:t,base64:r,...n})};return[new T({props:{handlePaste:(t,r)=>{let n=r.clipboardData?.items;if(!n)return!1;for(let i of n){if(i.kind!=="file")continue;let m=i.getAsFile();if(m)return e(m),!0}return!1},handleDrop:(t,r)=>{let n=r.dataTransfer?.files;return!n||n.length===0?!1:(e(n[0]),!0)}}})]}});function O(e){let t=new B({element:e.element,extensions:[...u,f({onCommand:e.onCommand,suggestionDom:e.suggestionDom,bridge:e.bridge}),H.configure({placeholder:e.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),p,v.configure({keepMarks:!0}),y.configure({onImageInput:e.onImageInput,onFileReject:e.onFileReject})],content:e.content??"",onUpdate:({editor:r})=>{e.onUpdate(r.getText())}});return{editor:t,destroy:()=>t.destroy(),getHtml:()=>t.getHTML()}}export{O as createMentionEditor};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/editor.ts","../../src/schema/base.ts","../../src/mention/index.ts","../../src/mention/renderer.ts","../../src/mention/suggestion.ts","../../src/extensions/keyboard.ts","../../src/extensions/imageInput.ts","../../src/utils/file.ts"],"sourcesContent":["import { Editor } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport { createMentionExtension } from './mention/index'\nimport type { MentionBridge, MentionItem } from './mention/types'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { KeyboardExtension } from './extensions/keyboard'\nimport HardBreak from '@tiptap/extension-hard-break'\nimport { ImageInputPayload } from './utils/file'\nimport { ImageInputExtension } from './extensions/imageInput'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n suggestionDom: HTMLElement,\n bridge: MentionBridge,\n onUpdate: (text: string) => void\n content?: string\n onCommand?: (item: MentionItem) => void,\n placeholder?: string\n\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\nexport interface MentionEditor {\n editor: Editor\n getHtml: () => string\n destroy: () => void\n\n}\n\nexport function createMentionEditor(options: MentionEditorOptions): MentionEditor {\n const editor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n createMentionExtension({\n onCommand: options.onCommand,\n suggestionDom: options.suggestionDom,\n bridge: options.bridge\n }),\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n KeyboardExtension,\n HardBreak.configure({\n keepMarks: true, // 保留样式(加粗 / mention)\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n ],\n content: options.content ?? '',\n onUpdate: ({ editor }) => {\n options.onUpdate(editor.getText())\n }\n })\n\n return {\n editor,\n destroy: () => editor.destroy(),\n getHtml: () => editor.getHTML(),\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import Mention from '@tiptap/extension-mention'\nimport { createSuggestion } from './suggestion'\nimport type { MentionBridge, MentionItem } from './types'\n\ninterface MentionExtensionOptions {\n onCommand?: (item: MentionItem) => void,\n suggestionDom: HTMLElement,\n bridge: MentionBridge,\n}\n\nexport function createMentionExtension(options: MentionExtensionOptions) {\n return Mention.configure({\n HTMLAttributes: {\n class: 'mention',\n 'data-type': 'mention',\n },\n suggestion: createSuggestion(options),\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n },\n node.attrs.label,\n ]\n },\n })\n}\n","import type { MentionItem, MentionBridge } from './types'\nimport { Editor } from '@tiptap/core'\n\nexport function createMentionRenderer(options: {\n suggestionDom: HTMLElement\n bridge: MentionBridge\n}) {\n return () => {\n let clientRect: DOMRect | null = null\n let range: { from: number; to: number } | null = null\n let mentionItems: MentionItem[] = []\n let selectedIndex = 0\n let command: (item: MentionItem) => void\n let editor: Editor | null = null\n\n function render() {\n syncBridge()\n document.body.appendChild(options.suggestionDom)\n position(options.suggestionDom, clientRect)\n }\n\n const syncBridge = () => {\n options.bridge.selectedIndex = selectedIndex\n }\n\n options.bridge.confirm = (index: number) => {\n confirmItem(index)\n }\n\n function confirmItem(index: number) {\n const item = mentionItems[index]\n if (!item) return\n\n if (item.disabled) {\n // editor?.chain().focus().insertContent(' ').run()\n options.suggestionDom.remove()\n } else {\n if (range) {\n editor.chain().focus().deleteRange(range).run()\n range = null\n }\n command(item)\n }\n }\n\n\n return {\n onStart(props: any) {\n editor = props.editor\n range = props.range\n clientRect = props.clientRect()\n mentionItems = options.bridge.items\n command = props.command\n selectedIndex = 0\n render()\n },\n\n onUpdate(props: any) {\n mentionItems = options.bridge.items\n range = props.range\n\n },\n\n onKeyDown({ event }: { event: KeyboardEvent }) {\n if (event.key === 'ArrowDown') {\n selectedIndex = (selectedIndex + 1) % mentionItems.length\n syncBridge()\n return true\n }\n if (event.key === 'ArrowUp') {\n selectedIndex = (selectedIndex - 1 + mentionItems.length) % mentionItems.length\n syncBridge()\n return true\n }\n if (event.key === 'Enter') {\n\n confirmItem(selectedIndex)\n return true\n }\n return false\n },\n\n onExit() {\n options.suggestionDom.remove()\n },\n }\n }\n}\n\nfunction position(el: HTMLElement, rect: DOMRect) {\n el.style.position = 'absolute'\n el.style.left = `${rect.left}px`\n el.style.top = `${rect.bottom + 4}px`\n}\n","import type { Editor } from '@tiptap/core'\nimport type { MentionBridge, MentionItem } from './types'\nimport { createMentionRenderer } from './renderer'\n\ninterface SuggestionOptions {\n onCommand?: (item: MentionItem) => void,\n suggestionDom: HTMLElement,\n bridge: MentionBridge,\n}\n\ninterface CommandProps {\n editor: Editor\n range: { from: number; to: number }\n props: MentionItem\n}\n\nexport function createSuggestion(options: SuggestionOptions) {\n return {\n char: '/',\n startOfLine: false,\n\n items: ({ query }: { query: string }): MentionItem[] => {\n options.bridge.setQuery(query)\n return options.bridge.items\n },\n\n command: ({ editor, range, props }: CommandProps) => {\n \n editor.chain().focus().insertContent({\n type: 'mention',\n attrs: {\n id: props.id,\n label: props.label,\n },\n }).run()\n \n options.onCommand?.(props)\n },\n\n render: createMentionRenderer({\n suggestionDom: options.suggestionDom,\n bridge: options.bridge\n }),\n\n }\n}\n","// extensions/keyboard.ts\nimport { Extension } from '@tiptap/core'\n\nexport const KeyboardExtension = Extension.create({\n name: 'customKeyboard',\n\n addKeyboardShortcuts() {\n return {\n // Shift + Enter = 换行\n 'Shift-Enter': ({ editor }) => {\n return editor.commands.setHardBreak()\n },\n // Enter = 只触发事件,不换行\n Enter: ({ editor }) => {\n editor.emit('enter')\n return false // 阻止默认换行\n },\n }\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n ImageInputPayload,\n} from '../utils/file'\n\nexport interface ImageInputOptions {\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export interface ImageInputPayload {\n file: File\n base64: string\n width?: number\n height?: number\n}\n\nexport function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n"],"mappings":"AAAA,OAAS,UAAAA,MAAc,eCAvB,OAAOC,MAAc,6BACrB,OAAOC,MAAe,8BACtB,OAAOC,MAAU,yBAEV,IAAMC,EAAiB,CAC1BH,EACAC,EACAC,CACJ,ECRA,OAAOE,MAAa,4BCGb,SAASC,EAAsBC,EAGnC,CACC,MAAO,IAAM,CACT,IAAIC,EAA6B,KAC7BC,EAA6C,KAC7CC,EAA8B,CAAC,EAC/BC,EAAgB,EAChBC,EACAC,EAAwB,KAE5B,SAASC,GAAS,CACdC,EAAW,EACX,SAAS,KAAK,YAAYR,EAAQ,aAAa,EAC/CS,EAAST,EAAQ,cAAeC,CAAU,CAC9C,CAEA,IAAMO,EAAa,IAAM,CACrBR,EAAQ,OAAO,cAAgBI,CACnC,EAEAJ,EAAQ,OAAO,QAAWU,GAAkB,CACxCC,EAAYD,CAAK,CACrB,EAEA,SAASC,EAAYD,EAAe,CAChC,IAAME,EAAOT,EAAaO,CAAK,EAC1BE,IAEDA,EAAK,SAELZ,EAAQ,cAAc,OAAO,GAEzBE,IACAI,EAAO,MAAM,EAAE,MAAM,EAAE,YAAYJ,CAAK,EAAE,IAAI,EAC9CA,EAAQ,MAEZG,EAAQO,CAAI,GAEpB,CAGA,MAAO,CACH,QAAQC,EAAY,CAChBP,EAASO,EAAM,OACfX,EAAQW,EAAM,MACdZ,EAAaY,EAAM,WAAW,EAC9BV,EAAeH,EAAQ,OAAO,MAC9BK,EAAUQ,EAAM,QAChBT,EAAgB,EAChBG,EAAO,CACX,EAEA,SAASM,EAAY,CACjBV,EAAeH,EAAQ,OAAO,MAC9BE,EAAQW,EAAM,KAElB,EAEA,UAAU,CAAE,MAAAC,CAAM,EAA6B,CAC3C,OAAIA,EAAM,MAAQ,aACdV,GAAiBA,EAAgB,GAAKD,EAAa,OACnDK,EAAW,EACJ,IAEPM,EAAM,MAAQ,WACdV,GAAiBA,EAAgB,EAAID,EAAa,QAAUA,EAAa,OACzEK,EAAW,EACJ,IAEPM,EAAM,MAAQ,SAEdH,EAAYP,CAAa,EAClB,IAEJ,EACX,EAEA,QAAS,CACLJ,EAAQ,cAAc,OAAO,CACjC,CACJ,CACJ,CACJ,CAEA,SAASS,EAASM,EAAiBC,EAAe,CAC9CD,EAAG,MAAM,SAAW,WACpBA,EAAG,MAAM,KAAO,GAAGC,EAAK,IAAI,KAC5BD,EAAG,MAAM,IAAM,GAAGC,EAAK,OAAS,CAAC,IACrC,CC7EO,SAASC,EAAiBC,EAA4B,CACzD,MAAO,CACH,KAAM,IACN,YAAa,GAEb,MAAO,CAAC,CAAE,MAAAC,CAAM,KACZD,EAAQ,OAAO,SAASC,CAAK,EACtBD,EAAQ,OAAO,OAG1B,QAAS,CAAC,CAAE,OAAAE,EAAQ,MAAAC,EAAO,MAAAC,CAAM,IAAoB,CAEjDF,EAAO,MAAM,EAAE,MAAM,EAAE,cAAc,CACjC,KAAM,UACN,MAAO,CACH,GAAIE,EAAM,GACV,MAAOA,EAAM,KACjB,CACJ,CAAC,EAAE,IAAI,EAEPJ,EAAQ,YAAYI,CAAK,CAC7B,EAEA,OAAQC,EAAsB,CAC1B,cAAeL,EAAQ,cACvB,OAAQA,EAAQ,MACpB,CAAC,CAEL,CACJ,CFnCO,SAASM,EAAuBC,EAAkC,CACrE,OAAOC,EAAQ,UAAU,CACrB,eAAgB,CACZ,MAAO,UACP,YAAa,SACjB,EACA,WAAYC,EAAiBF,CAAO,EACpC,WAAW,CAAE,KAAAG,CAAK,EAAG,CACjB,MAAO,CACH,OACA,CACI,MAAO,UACP,UAAWA,EAAK,MAAM,GACtB,aAAcA,EAAK,MAAM,KAC7B,EACAA,EAAK,MAAM,KACf,CACJ,CACJ,CAAC,CACL,CFzBA,OAAOC,MAAiB,gCKHxB,OAAS,aAAAC,MAAiB,eAEnB,IAAMC,EAAoBD,EAAU,OAAO,CAC9C,KAAM,iBAEN,sBAAuB,CACnB,MAAO,CAEH,cAAe,CAAC,CAAE,OAAAE,CAAO,IACdA,EAAO,SAAS,aAAa,EAGxC,MAAO,CAAC,CAAE,OAAAA,CAAO,KACbA,EAAO,KAAK,OAAO,EACZ,GAEf,CACJ,CACJ,CAAC,ELbD,OAAOC,MAAe,+BMNtB,OAAS,aAAAC,MAAiB,eAC1B,OAAS,UAAAC,MAAc,oBCMhB,SAASC,EAAYC,EAAY,CACpC,OAAOA,EAAK,KAAK,WAAW,QAAQ,CACxC,CAEO,SAASC,EAAaD,EAA6B,CACtD,OAAO,IAAI,QAAQ,CAACE,EAASC,IAAW,CACpC,IAAMC,EAAS,IAAI,WACnBA,EAAO,OAAS,IAAMF,EAAQE,EAAO,MAAgB,EACrDA,EAAO,QAAUD,EACjBC,EAAO,cAAcJ,CAAI,CAC7B,CAAC,CACL,CAEO,SAASK,EAAaC,EAA4D,CACrF,OAAO,IAAI,QAAQJ,GAAW,CAC1B,IAAMK,EAAM,IAAI,MAChBA,EAAI,OAAS,IAAML,EAAQ,CAAE,MAAOK,EAAI,MAAO,OAAQA,EAAI,MAAO,CAAC,EACnEA,EAAI,IAAMD,CACd,CAAC,CACL,CDZO,IAAME,EAAsBC,EAAU,OAA0B,CACnE,KAAM,aAEN,YAAa,CACT,MAAO,CACH,aAAc,OACd,aAAc,MAClB,CACJ,EAEA,uBAAwB,CACpB,IAAMC,EAAa,MAAOC,GAAe,CACrC,GAAI,CAACC,EAAYD,CAAI,EAAG,CACpB,KAAK,QAAQ,eAAeA,CAAI,EAChC,MACJ,CAEA,IAAME,EAAS,MAAMC,EAAaH,CAAI,EAChCI,EAAO,MAAMC,EAAaH,CAAM,EAEtC,KAAK,QAAQ,eAAe,CACxB,KAAAF,EACA,OAAAE,EACA,GAAGE,CACP,CAAC,CACL,EAEA,MAAO,CACH,IAAIE,EAAO,CACP,MAAO,CACH,YAAa,CAACC,EAAGC,IAAU,CACvB,IAAMC,EAAQD,EAAM,eAAe,MACnC,GAAI,CAACC,EAAO,MAAO,GAEnB,QAAWC,KAAQD,EAAO,CACtB,GAAIC,EAAK,OAAS,OAAQ,SAC1B,IAAMV,EAAOU,EAAK,UAAU,EAC5B,GAAKV,EAEL,OAAAD,EAAWC,CAAI,EACR,EACX,CAEA,MAAO,EACX,EAEA,WAAY,CAACO,EAAGC,IAAU,CACtB,IAAMG,EAAQH,EAAM,cAAc,MAClC,MAAI,CAACG,GAASA,EAAM,SAAW,EAAU,IAEzCZ,EAAWY,EAAM,CAAC,CAAC,EACZ,GACX,CACJ,CACJ,CAAC,CACL,CACJ,CACJ,CAAC,ENzCM,SAASC,EAAoBC,EAA8C,CAC9E,IAAMC,EAAS,IAAIC,EAAO,CACtB,QAASF,EAAQ,QACjB,WAAY,CACR,GAAGG,EACHC,EAAuB,CACnB,UAAWJ,EAAQ,UACnB,cAAeA,EAAQ,cACvB,OAAQA,EAAQ,MACpB,CAAC,EACDK,EAAY,UAAU,CAClB,YAAaL,EAAQ,aAAe,wBACpC,iBAAkB,iBACtB,CAAC,EACDM,EACAC,EAAU,UAAU,CAChB,UAAW,EACf,CAAC,EACDC,EAAoB,UAAU,CAC1B,aAAcR,EAAQ,aACtB,aAAcA,EAAQ,YAC1B,CAAC,CACL,EACA,QAASA,EAAQ,SAAW,GAC5B,SAAU,CAAC,CAAE,OAAAC,CAAO,IAAM,CACtBD,EAAQ,SAASC,EAAO,QAAQ,CAAC,CACrC,CACJ,CAAC,EAED,MAAO,CACH,OAAAA,EACA,QAAS,IAAMA,EAAO,QAAQ,EAC9B,QAAS,IAAMA,EAAO,QAAQ,CAClC,CACJ","names":["Editor","Document","Paragraph","Text","baseExtensions","Mention","createMentionRenderer","options","clientRect","range","mentionItems","selectedIndex","command","editor","render","syncBridge","position","index","confirmItem","item","props","event","el","rect","createSuggestion","options","query","editor","range","props","createMentionRenderer","createMentionExtension","options","Mention","createSuggestion","node","Placeholder","Extension","KeyboardExtension","editor","HardBreak","Extension","Plugin","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","Extension","handleFile","file","isImageFile","base64","fileToBase64","size","getImageSize","Plugin","_","event","items","item","files","createMentionEditor","options","editor","Editor","baseExtensions","createMentionExtension","Placeholder","KeyboardExtension","HardBreak","ImageInputExtension"]}