@seorii/tiptap 0.4.6 → 0.4.8

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.
@@ -2,5 +2,5 @@ import { type SlashItem } from './stores.svelte';
2
2
  import { type SuggestionOptions } from '@tiptap/suggestion';
3
3
  import type { Editor } from '@tiptap/core';
4
4
  export declare const emoji: Omit<SuggestionOptions<SlashItem>, 'editor'>;
5
- declare const _default: (editor: Editor) => import("prosemirror-state").Plugin<any>;
5
+ declare const _default: (editor: Editor) => import("@tiptap/pm/state").Plugin<any>;
6
6
  export default _default;
@@ -4,5 +4,5 @@ import { type SuggestionOptions } from '@tiptap/suggestion';
4
4
  type DetailInput = Exclude<SlashDetail, null | 'emoji'>;
5
5
  export declare function getDetail(editor: Editor, range: Range, option: DetailInput): void;
6
6
  export declare const suggest: Omit<SuggestionOptions<SlashGroup>, 'editor'>;
7
- declare const _default: (editor: Editor) => import("prosemirror-state").Plugin<any>;
7
+ declare const _default: (editor: Editor) => import("@tiptap/pm/state").Plugin<any>;
8
8
  export default _default;
@@ -1,5 +1,5 @@
1
- import { Plugin } from 'prosemirror-state';
2
- import type { EditorView } from 'prosemirror-view';
1
+ import { Plugin } from '@tiptap/pm/state';
2
+ import type { EditorView } from '@tiptap/pm/view';
3
3
  export type UploadFn = (image: File) => Promise<string>;
4
4
  export declare const fallbackUpload: (image: File) => Promise<string>;
5
5
  export declare const releaseObjectUrlOnImageSettled: (view: EditorView, src: string) => void;
@@ -1,4 +1,4 @@
1
- import { Plugin } from 'prosemirror-state';
1
+ import { Plugin } from '@tiptap/pm/state';
2
2
  import { insertUploadSkeleton } from '../upload/skeleton';
3
3
  export const fallbackUpload = async (image) => URL.createObjectURL(image);
4
4
  const OBJECT_URL_PREFIX = 'blob:';
@@ -2,7 +2,7 @@
2
2
  // https://github.com/ueberdosis/tiptap/issues/1036#issuecomment-981094752
3
3
  // https://github.com/django-tiptap/django_tiptap/blob/main/django_tiptap/templates/forms/tiptap_textarea.html#L453-L602
4
4
  import { Extension, isList } from '@tiptap/core';
5
- import { TextSelection, Transaction } from 'prosemirror-state';
5
+ import { TextSelection, Transaction } from '@tiptap/pm/state';
6
6
  export const Indent = Extension.create({
7
7
  name: 'indent',
8
8
  addOptions() {
@@ -0,0 +1,3 @@
1
+ import { Extension } from '@tiptap/core';
2
+ declare const _default: Extension<any, any>;
3
+ export default _default;
@@ -0,0 +1,147 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { Fragment, Slice } from '@tiptap/pm/model';
3
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
4
+ export default Extension.create({
5
+ name: 'mathPaste',
6
+ addProseMirrorPlugins() {
7
+ return [
8
+ new Plugin({
9
+ key: new PluginKey('mathPaste'),
10
+ props: {
11
+ transformPasted: (slice, view) => {
12
+ const { schema } = view.state;
13
+ const mathInline = schema.nodes.math_inline;
14
+ const mathDisplay = schema.nodes.math_display;
15
+ if (!mathInline && !mathDisplay)
16
+ return slice;
17
+ let changed = false;
18
+ let blockChanged = false;
19
+ const transformFragment = (fragment, parent) => {
20
+ const nodes = [];
21
+ for (let index = 0; index < fragment.childCount; index++) {
22
+ const node = fragment.child(index);
23
+ if (parent?.type.spec.code ||
24
+ node.type.spec.code ||
25
+ node.type.name.startsWith('math_')) {
26
+ nodes.push(node);
27
+ continue;
28
+ }
29
+ if (mathDisplay && node.isTextblock && node.textContent.trim() === '$$') {
30
+ const source = [];
31
+ let endIndex = -1;
32
+ for (let offset = index + 1; offset < fragment.childCount; offset++) {
33
+ const candidate = fragment.child(offset);
34
+ if (candidate.type.spec.code ||
35
+ candidate.type.name.startsWith('math_') ||
36
+ !candidate.isTextblock) {
37
+ break;
38
+ }
39
+ const text = candidate.textContent;
40
+ if (text.trim() === '$$') {
41
+ endIndex = offset;
42
+ break;
43
+ }
44
+ source.push(text);
45
+ }
46
+ const text = source.join('\n').trim();
47
+ if (endIndex > -1 && text.length) {
48
+ nodes.push(mathDisplay.create(null, schema.text(text)));
49
+ index = endIndex;
50
+ changed = true;
51
+ blockChanged = true;
52
+ continue;
53
+ }
54
+ }
55
+ if (mathDisplay && node.isTextblock) {
56
+ const match = /^\s*\$\$([\s\S]*?)\$\$\s*$/.exec(node.textContent);
57
+ const text = match?.[1]?.trim();
58
+ if (text) {
59
+ nodes.push(mathDisplay.create(null, schema.text(text)));
60
+ changed = true;
61
+ blockChanged = true;
62
+ continue;
63
+ }
64
+ }
65
+ if (node.isText) {
66
+ if (!mathInline || node.marks.some((mark) => mark.type.spec.code)) {
67
+ nodes.push(node);
68
+ continue;
69
+ }
70
+ const text = node.text || '';
71
+ const inlineNodes = [];
72
+ let cursor = 0;
73
+ let searchFrom = 0;
74
+ while (searchFrom < text.length) {
75
+ const open = text.indexOf('$', searchFrom);
76
+ if (open === -1)
77
+ break;
78
+ const beforeOpen = text[open - 1];
79
+ const afterOpen = text[open + 1];
80
+ if (beforeOpen === '\\' ||
81
+ beforeOpen === '$' ||
82
+ afterOpen === '$' ||
83
+ !afterOpen ||
84
+ /\s/.test(afterOpen)) {
85
+ searchFrom = open + 1;
86
+ continue;
87
+ }
88
+ let close = open + 1;
89
+ while (close < text.length) {
90
+ close = text.indexOf('$', close);
91
+ if (close === -1)
92
+ break;
93
+ const beforeClose = text[close - 1];
94
+ const afterClose = text[close + 1];
95
+ if (beforeClose === '\\' ||
96
+ beforeClose === '$' ||
97
+ afterClose === '$' ||
98
+ /\s/.test(beforeClose)) {
99
+ close++;
100
+ continue;
101
+ }
102
+ break;
103
+ }
104
+ if (close === -1)
105
+ break;
106
+ const source = text.slice(open + 1, close);
107
+ if (!source.trim()) {
108
+ searchFrom = close + 1;
109
+ continue;
110
+ }
111
+ if (open > cursor)
112
+ inlineNodes.push(schema.text(text.slice(cursor, open), node.marks));
113
+ inlineNodes.push(mathInline.create(null, schema.text(source), node.marks));
114
+ cursor = close + 1;
115
+ searchFrom = cursor;
116
+ }
117
+ if (!inlineNodes.length) {
118
+ nodes.push(node);
119
+ continue;
120
+ }
121
+ if (cursor < text.length)
122
+ inlineNodes.push(schema.text(text.slice(cursor), node.marks));
123
+ nodes.push(...inlineNodes);
124
+ changed = true;
125
+ continue;
126
+ }
127
+ if (!node.content.size) {
128
+ nodes.push(node);
129
+ continue;
130
+ }
131
+ const content = transformFragment(node.content, node);
132
+ nodes.push(content.eq(node.content) ? node : node.copy(content));
133
+ }
134
+ return Fragment.fromArray(nodes);
135
+ };
136
+ const content = transformFragment(slice.content, null);
137
+ if (!changed)
138
+ return slice;
139
+ return blockChanged
140
+ ? new Slice(content, 0, 0)
141
+ : new Slice(content, slice.openStart, slice.openEnd);
142
+ }
143
+ }
144
+ })
145
+ ];
146
+ }
147
+ });
@@ -1,4 +1,4 @@
1
- import { canJoin } from 'prosemirror-transform';
1
+ import { canJoin } from '@tiptap/pm/transform';
2
2
  import { getNodeType, findParentNode, isList } from '@tiptap/core';
3
3
  const joinListBackwards = (tr, listType) => {
4
4
  const list = findParentNode((node) => node.type === listType)(tr.selection);
@@ -1,5 +1,5 @@
1
1
  import { isColumnSelected, isRowSelected, isTableSelected } from './util';
2
- import { TableMap } from 'prosemirror-tables';
2
+ import { TableMap } from '@tiptap/pm/tables';
3
3
  export const deleteTable = ({ editor }) => {
4
4
  const { selection } = editor.state;
5
5
  if (!selection || !selection.$anchorCell)
@@ -1,7 +1,7 @@
1
1
  import { Table as BuiltInTable } from '@tiptap/extension-table';
2
- import { Plugin } from 'prosemirror-state';
3
- import { tableEditing, columnResizing } from 'prosemirror-tables';
4
- import { Decoration, DecorationSet } from 'prosemirror-view';
2
+ import { Plugin } from '@tiptap/pm/state';
3
+ import { tableEditing, columnResizing } from '@tiptap/pm/tables';
4
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
5
5
  import deleteTable from './deleteTable';
6
6
  import './style.css';
7
7
  const resolveTableElement = (view, pos) => {
@@ -1,6 +1,6 @@
1
1
  import { TableCell as BuiltInTableCell } from '@tiptap/extension-table-cell';
2
- import { Plugin } from 'prosemirror-state';
3
- import { Decoration, DecorationSet } from 'prosemirror-view';
2
+ import { Plugin } from '@tiptap/pm/state';
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
4
  import { getCellsInColumn, isRowSelected, isTableSelected, selectRow, selectTable } from '../util';
5
5
  export default BuiltInTableCell.extend({
6
6
  addAttributes() {
@@ -1,6 +1,6 @@
1
1
  import { TableHeader as BuiltInTableHeader } from '@tiptap/extension-table-header';
2
- import { Plugin } from 'prosemirror-state';
3
- import { Decoration, DecorationSet } from 'prosemirror-view';
2
+ import { Plugin } from '@tiptap/pm/state';
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
4
  import { getCellsInRow, isColumnSelected, selectColumn } from '../util';
5
5
  export default BuiltInTableHeader.extend({
6
6
  addAttributes() {
@@ -1,6 +1,6 @@
1
- import type { Node, ResolvedPos } from 'prosemirror-model';
2
- import type { EditorState, Selection, Transaction } from 'prosemirror-state';
3
- import { CellSelection, type TableRect } from 'prosemirror-tables';
1
+ import type { Node, ResolvedPos } from '@tiptap/pm/model';
2
+ import type { EditorState, Selection, Transaction } from '@tiptap/pm/state';
3
+ import { CellSelection, type TableRect } from '@tiptap/pm/tables';
4
4
  export declare const isRectSelected: (rect: any) => (selection: CellSelection) => boolean;
5
5
  export declare const findTable: (selection: Selection) => {
6
6
  pos: number;
@@ -1,5 +1,5 @@
1
1
  import { findParentNode } from '@tiptap/core';
2
- import { CellSelection, selectionCell, TableMap } from 'prosemirror-tables';
2
+ import { CellSelection, selectionCell, TableMap } from '@tiptap/pm/tables';
3
3
  export const isRectSelected = (rect) => (selection) => {
4
4
  const map = TableMap.get(selection.$anchorCell.node(-1));
5
5
  const start = selection.$anchorCell.start(-1);
@@ -1,5 +1,5 @@
1
1
  import { Node, mergeAttributes } from '@tiptap/core';
2
- import { Plugin, PluginKey } from 'prosemirror-state';
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
3
3
  const youtubeRegExp = /^(?:(?:https?:)?\/\/)?(?:www\.)?(?:m\.)?(?:youtu(?:be)?\.com\/(?:v\/|embed\/|watch(?:\/|\?v=))|youtu\.be\/)((?:\w|-){11})(?:\S+)?$/;
4
4
  const youtubeExtractId = (url) => {
5
5
  const match = youtubeRegExp.exec(url.trim());
@@ -94,6 +94,7 @@
94
94
  allowedTags: sanitizeHtml.defaults.allowedTags.concat([
95
95
  'img',
96
96
  'math-inline',
97
+ 'math-display',
97
98
  'math-node',
98
99
  'iframe',
99
100
  'lite-youtube',
@@ -1,9 +1,22 @@
1
1
  import { Editor, Extension, mergeAttributes } from '@tiptap/core';
2
2
  import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight';
3
3
  import { all, createLowlight } from 'lowlight';
4
+ import { Blockquote } from '@tiptap/extension-blockquote';
5
+ import { Bold } from '@tiptap/extension-bold';
6
+ import { BulletList } from '@tiptap/extension-bullet-list';
4
7
  import Code from '@tiptap/extension-code';
8
+ import { Document } from '@tiptap/extension-document';
9
+ import { Gapcursor } from '@tiptap/extension-gapcursor';
10
+ import { HardBreak } from '@tiptap/extension-hard-break';
11
+ import { Heading } from '@tiptap/extension-heading';
12
+ import { History } from '@tiptap/extension-history';
13
+ import { HorizontalRule } from '@tiptap/extension-horizontal-rule';
14
+ import { Italic } from '@tiptap/extension-italic';
15
+ import { ListItem } from '@tiptap/extension-list-item';
16
+ import { Paragraph } from '@tiptap/extension-paragraph';
17
+ import { Strike } from '@tiptap/extension-strike';
18
+ import { Text } from '@tiptap/extension-text';
5
19
  import Image from '../plugin/image';
6
- import StarterKit from '@tiptap/starter-kit';
7
20
  import Underline from '@tiptap/extension-underline';
8
21
  import Highlight from '@tiptap/extension-highlight';
9
22
  import Link from '@tiptap/extension-link';
@@ -24,6 +37,7 @@ import Embed from '../plugin/embed';
24
37
  import UploadSkeleton from '../plugin/upload/skeleton';
25
38
  // @ts-ignore
26
39
  import { MathInline, MathBlock } from '@seorii/prosemirror-math/tiptap';
40
+ import MathPaste from '../plugin/mathPaste';
27
41
  import Youtube from '../plugin/youtube';
28
42
  import Placeholder from '@tiptap/extension-placeholder';
29
43
  import columns from '../plugin/columns';
@@ -213,54 +227,79 @@ const CodeBlockWithLanguageSelect = CodeBlockLowlight.extend({
213
227
  };
214
228
  }
215
229
  });
216
- const extensions = (placeholder, plugins, crossorigin, codeBlockLanguageLabels) => [
217
- CodeBlockWithLanguageSelect.configure({
218
- lowlight: lowlight(),
219
- languageLabelMap: codeBlockLanguageLabels
220
- }),
221
- slashKeymap,
222
- Image(crossorigin),
223
- Youtube,
224
- StarterKit,
225
- Underline,
226
- Highlight.configure({ multicolor: true }),
227
- Link.configure({
228
- openOnClick: true,
229
- protocols: [
230
- 'ftp',
231
- 'mailto',
232
- {
233
- scheme: 'tel',
234
- optionalSlashes: true
230
+ const extensions = (placeholder, plugins, crossorigin, codeBlockLanguageLabels) => {
231
+ const pluginNames = new Set(plugins
232
+ .map((plugin) => plugin?.name)
233
+ .filter((name) => typeof name === 'string'));
234
+ const baseExtensions = [
235
+ Bold,
236
+ Blockquote,
237
+ BulletList,
238
+ Code.extend({
239
+ renderHTML({ HTMLAttributes }) {
240
+ return ['code', mergeAttributes(HTMLAttributes, { class: 'inline' })];
235
241
  }
236
- ]
237
- }),
238
- TextAlign.configure({ types: ['heading', 'paragraph', 'image'] }),
239
- DropCursor,
240
- orderedlist,
241
- MathInline,
242
- MathBlock,
243
- ...columns,
244
- table,
245
- tableHeader,
246
- tableRow,
247
- tableCell,
248
- Superscript,
249
- Subscript,
250
- Indent,
251
- Color,
252
- TextStyle,
253
- UploadSkeleton,
254
- Iframe,
255
- Embed,
256
- Code.extend({
257
- renderHTML({ HTMLAttributes }) {
258
- return ['code', mergeAttributes(HTMLAttributes, { class: 'inline' })];
259
- }
260
- }),
261
- Placeholder.configure({ placeholder }),
262
- ...plugins
263
- ];
242
+ }),
243
+ CodeBlockWithLanguageSelect.configure({
244
+ lowlight: lowlight(),
245
+ languageLabelMap: codeBlockLanguageLabels
246
+ }),
247
+ Document,
248
+ DropCursor,
249
+ Gapcursor,
250
+ HardBreak,
251
+ Heading,
252
+ History,
253
+ HorizontalRule,
254
+ Italic,
255
+ ListItem,
256
+ orderedlist,
257
+ Paragraph,
258
+ Strike,
259
+ Text,
260
+ slashKeymap,
261
+ Image(crossorigin),
262
+ Youtube,
263
+ Underline,
264
+ Highlight.configure({ multicolor: true }),
265
+ Link.configure({
266
+ openOnClick: true,
267
+ protocols: [
268
+ 'ftp',
269
+ 'mailto',
270
+ {
271
+ scheme: 'tel',
272
+ optionalSlashes: true
273
+ }
274
+ ]
275
+ }),
276
+ TextAlign.configure({ types: ['heading', 'paragraph', 'image'] }),
277
+ MathInline,
278
+ MathBlock,
279
+ MathPaste,
280
+ ...columns,
281
+ table,
282
+ tableHeader,
283
+ tableRow,
284
+ tableCell,
285
+ Superscript,
286
+ Subscript,
287
+ Indent,
288
+ Color,
289
+ TextStyle,
290
+ UploadSkeleton,
291
+ Iframe,
292
+ Embed,
293
+ Placeholder.configure({ placeholder })
294
+ ];
295
+ return [
296
+ ...baseExtensions.filter((extension) => {
297
+ const name = extension?.name;
298
+ return typeof name !== 'string' || !pluginNames.has(name);
299
+ }),
300
+ ...plugins
301
+ ];
302
+ };
264
303
  export default (element, content, { placeholder = i18n('placeholder'), plugins = [], crossorigin, codeBlockLanguageLabels = {}, ...props } = {}) => {
265
304
  const tt = new Editor({
266
305
  element,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seorii/tiptap",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "svelte-kit sync && svelte-package",
@@ -45,41 +45,52 @@
45
45
  "type": "module",
46
46
  "dependencies": {
47
47
  "@justinribeiro/lite-youtube": "^1.9.0",
48
- "@seorii/prosemirror-math": "^0.4.2",
48
+ "@seorii/prosemirror-math": "^0.5.0",
49
49
  "@tiptap/core": "^2",
50
+ "@tiptap/extension-blockquote": "^2",
51
+ "@tiptap/extension-bold": "^2",
52
+ "@tiptap/extension-bullet-list": "^2",
50
53
  "@tiptap/extension-code": "^2",
51
54
  "@tiptap/extension-code-block": "^2",
52
55
  "@tiptap/extension-code-block-lowlight": "^2",
53
56
  "@tiptap/extension-color": "^2",
57
+ "@tiptap/extension-document": "^2",
54
58
  "@tiptap/extension-dropcursor": "^2",
59
+ "@tiptap/extension-gapcursor": "^2",
60
+ "@tiptap/extension-hard-break": "^2",
61
+ "@tiptap/extension-heading": "^2",
55
62
  "@tiptap/extension-highlight": "^2",
63
+ "@tiptap/extension-history": "^2",
64
+ "@tiptap/extension-horizontal-rule": "^2",
56
65
  "@tiptap/extension-image": "^2",
66
+ "@tiptap/extension-italic": "^2",
57
67
  "@tiptap/extension-link": "^2",
68
+ "@tiptap/extension-list-item": "^2",
58
69
  "@tiptap/extension-ordered-list": "^2",
70
+ "@tiptap/extension-paragraph": "^2",
59
71
  "@tiptap/extension-placeholder": "^2",
72
+ "@tiptap/extension-strike": "^2",
60
73
  "@tiptap/extension-subscript": "^2",
61
74
  "@tiptap/extension-superscript": "^2",
62
75
  "@tiptap/extension-table": "^2",
63
76
  "@tiptap/extension-table-cell": "^2",
64
77
  "@tiptap/extension-table-header": "^2",
65
78
  "@tiptap/extension-table-row": "^2",
79
+ "@tiptap/extension-text": "^2",
66
80
  "@tiptap/extension-text-align": "^2",
67
81
  "@tiptap/extension-text-style": "^2",
68
82
  "@tiptap/extension-underline": "^2",
69
- "@tiptap/html": "^2",
70
83
  "@tiptap/pm": "^2",
71
- "@tiptap/starter-kit": "^2",
72
84
  "@tiptap/suggestion": "^2",
73
85
  "emojis-keywords": "2.0.0",
74
86
  "emojis-list": "3.0.0",
75
87
  "lowlight": "^3.3.0",
76
88
  "nunui": "2.0.0-next.51",
77
- "prosemirror-commands": "^1.7.1",
78
- "prosemirror-model": "^1.25.4",
79
- "prosemirror-state": "^1.4.4",
80
- "prosemirror-tables": "^1.8.5",
81
- "prosemirror-transform": "^1.11.0",
82
- "prosemirror-view": "^1.41.6",
89
+ "prosemirror-commands": "1.7.1",
90
+ "prosemirror-model": "1.25.4",
91
+ "prosemirror-state": "1.4.4",
92
+ "prosemirror-transform": "1.11.0",
93
+ "prosemirror-view": "1.41.6",
83
94
  "sanitize-html": "^2.17.1",
84
95
  "svelte-awesome-color-picker": "^4.1.2",
85
96
  "svelte-tiptap": "^2",