@seorii/tiptap 0.3.0-next.8 → 0.3.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.
Files changed (45) hide show
  1. package/dist/i18n/index.d.ts +106 -6
  2. package/dist/i18n/index.js +56 -11
  3. package/dist/plugin/command/emoji.d.ts +3 -17
  4. package/dist/plugin/command/emoji.js +51 -24
  5. package/dist/plugin/command/stores.svelte.d.ts +52 -0
  6. package/dist/plugin/command/stores.svelte.js +69 -0
  7. package/dist/plugin/command/suggest.d.ts +6 -19
  8. package/dist/plugin/command/suggest.js +133 -51
  9. package/dist/plugin/embed.d.ts +2 -2
  10. package/dist/plugin/embed.js +5 -1
  11. package/dist/plugin/image/dragdrop.d.ts +2 -0
  12. package/dist/plugin/image/dragdrop.js +66 -2
  13. package/dist/plugin/image/index.js +3 -2
  14. package/dist/plugin/indent.js +0 -1
  15. package/dist/plugin/orderedlist/index.d.ts +1 -1
  16. package/dist/plugin/orderedlist/index.js +1 -1
  17. package/dist/plugin/orderedlist/{korean.scss → korean.css} +2 -2
  18. package/dist/plugin/table/index.d.ts +1 -1
  19. package/dist/plugin/table/index.js +19 -11
  20. package/dist/plugin/table/style/{cell.scss → cell.css} +6 -5
  21. package/dist/plugin/table/style/{grip.scss → grip.css} +14 -19
  22. package/dist/plugin/table/style/resize.css +28 -0
  23. package/dist/plugin/table/style/{table.scss → table.css} +15 -17
  24. package/dist/plugin/table/style.css +4 -0
  25. package/dist/plugin/table/tableCell/index.js +2 -4
  26. package/dist/plugin/table/tableHeader/index.js +1 -2
  27. package/dist/tiptap/Bubble.svelte +106 -71
  28. package/dist/tiptap/Bubble.svelte.d.ts +8 -6
  29. package/dist/tiptap/Command.svelte +160 -158
  30. package/dist/tiptap/Command.svelte.d.ts +2 -3
  31. package/dist/tiptap/Floating.svelte +51 -24
  32. package/dist/tiptap/Floating.svelte.d.ts +1 -0
  33. package/dist/tiptap/TipTap.svelte +215 -135
  34. package/dist/tiptap/TipTap.svelte.d.ts +7 -3
  35. package/dist/tiptap/ToolbarButton.svelte +30 -10
  36. package/dist/tiptap/ToolbarButton.svelte.d.ts +10 -6
  37. package/dist/tiptap/setMath.d.ts +2 -1
  38. package/dist/tiptap/setMath.js +74 -12
  39. package/dist/tiptap/tiptap.d.ts +9 -1
  40. package/dist/tiptap/tiptap.js +170 -16
  41. package/package.json +63 -57
  42. package/dist/plugin/command/stores.d.ts +0 -13
  43. package/dist/plugin/command/stores.js +0 -7
  44. package/dist/plugin/table/style/resize.scss +0 -26
  45. package/dist/plugin/table/style.scss +0 -4
@@ -1,12 +1,22 @@
1
1
  <script lang="ts">
2
- import { onMount, onDestroy, setContext, untrack } from 'svelte';
2
+ import { setContext, untrack } from 'svelte';
3
3
  import sanitizeHtml from 'sanitize-html';
4
4
  import '@seorii/prosemirror-math/style.css';
5
5
  import Bubble from './Bubble.svelte';
6
6
  import Floating from './Floating.svelte';
7
7
  import Command from './Command.svelte';
8
- import { slashItems, slashProps, slashVisible } from '../plugin/command/stores';
9
- import i18n from '../i18n';
8
+ import {
9
+ countSlashItems,
10
+ flattenSlashItems,
11
+ normalizeSlashIndex,
12
+ slashState
13
+ } from '../plugin/command/stores.svelte';
14
+ import {
15
+ I18N_CONTEXT,
16
+ setLocale as setI18nLocale,
17
+ translateWithLocale,
18
+ type I18nTranslate
19
+ } from '../i18n';
10
20
  import type { UploadFn } from '../plugin/image/dragdrop';
11
21
  import { fallbackUpload } from '../plugin/image/dragdrop';
12
22
  import { Render } from 'nunui';
@@ -22,11 +32,13 @@
22
32
  style?: string;
23
33
  blocks?: any[];
24
34
  placeholder?: string;
35
+ locale?: string;
25
36
  sanitize?: Record<string, any>;
26
37
  colors?: string[];
27
38
  bubble?: any;
28
39
  preloader?: any;
29
40
  crossorigin?: 'anonymous' | 'use-credentials';
41
+ codeBlockLanguageLabels?: Record<string, string>;
30
42
  };
31
43
 
32
44
  let {
@@ -39,7 +51,8 @@
39
51
  imageUpload = fallbackUpload,
40
52
  style = '',
41
53
  blocks = [],
42
- placeholder = i18n('placeholder'),
54
+ placeholder,
55
+ locale,
43
56
  sanitize = {},
44
57
  colors = [
45
58
  '#ef5350', //red
@@ -54,8 +67,11 @@
54
67
  bubble = null,
55
68
  preloader,
56
69
  crossorigin = 'anonymous',
70
+ codeBlockLanguageLabels = {}
57
71
  }: Props = $props();
58
72
 
73
+ const scopedI18n: I18nTranslate = (...args) => translateWithLocale(locale, ...args);
74
+
59
75
  const san = (body: string) =>
60
76
  sanitizeHtml(body || '', {
61
77
  ...(sanitize || {}),
@@ -87,13 +103,18 @@
87
103
  }
88
104
  });
89
105
 
90
- const tiptap = $state({ v: null as any });
106
+ const tiptap = $state({ v: null as any, c: 0 });
91
107
  setContext('editor', tiptap);
108
+ setContext(I18N_CONTEXT, scopedI18n);
92
109
  let element: Element,
93
110
  fullscreen = $state(false),
94
111
  mounted = $state(false),
95
112
  last = $state('');
96
113
 
114
+ $effect(() => {
115
+ setI18nLocale(locale);
116
+ });
117
+
97
118
  $effect(() => {
98
119
  if (tiptap.v) tiptap.v.setEditable(editable);
99
120
  });
@@ -111,11 +132,16 @@
111
132
  Promise.all([import('./tiptap'), import('@justinribeiro/lite-youtube')]).then(
112
133
  ([{ default: tt }]) => {
113
134
  if (!untrack(() => mounted)) return;
135
+ const editorPlaceholder = placeholder ?? scopedI18n('placeholder');
114
136
  tiptap.v = ref = tt(element, r, {
115
- placeholder,
137
+ placeholder: editorPlaceholder,
116
138
  editable,
117
- onTransaction: () => (tiptap.v = ref = tiptap.v),
139
+ onTransaction: () => {
140
+ tiptap.v = ref = tiptap.v;
141
+ tiptap.c++;
142
+ },
118
143
  crossorigin,
144
+ codeBlockLanguageLabels,
119
145
  ...options
120
146
  });
121
147
  tiptap.v.on('update', ({ editor: tiptap }: any) => {
@@ -147,29 +173,35 @@
147
173
  tiptap.v?.commands?.setContent?.(body);
148
174
  });
149
175
 
150
- let selectedIndex = $state(0);
151
- $effect(() => {
152
- if (!slashVisible) selectedIndex = 0;
153
- });
154
-
155
176
  function handleKeydown(event: KeyboardEvent) {
156
- if (!$slashVisible) return;
157
- let count = $slashItems.length;
158
- if (($slashItems[0] as any)?.list) count = $slashItems.reduce((acc, item) => acc + (item as any).list.length, 0);
177
+ if (event.defaultPrevented) return true;
178
+ if (!slashState.visible) return false;
179
+ const count = countSlashItems();
180
+ if (!count) return false;
181
+
182
+ if (event.key === 'Tab') {
183
+ event.preventDefault();
184
+ slashState.selectedIndex += event.shiftKey ? -1 : 1;
185
+ normalizeSlashIndex();
186
+ return true;
187
+ }
188
+
159
189
  if (event.key === 'ArrowUp') {
160
190
  event.preventDefault();
161
- selectedIndex = (selectedIndex + count - 1) % count;
191
+ slashState.selectedIndex -= 1;
192
+ normalizeSlashIndex();
162
193
  return true;
163
194
  }
164
195
  if (event.key === 'ArrowDown') {
165
196
  event.preventDefault();
166
- selectedIndex = (selectedIndex + 1) % count;
197
+ slashState.selectedIndex += 1;
198
+ normalizeSlashIndex();
167
199
  return true;
168
200
  }
169
201
 
170
202
  if (event.key === 'Enter') {
171
203
  event.preventDefault();
172
- selectItem(selectedIndex);
204
+ selectItem(slashState.selectedIndex);
173
205
  return true;
174
206
  }
175
207
 
@@ -177,13 +209,10 @@
177
209
  }
178
210
 
179
211
  function selectItem(index: number) {
180
- const item = ($slashItems[0] as any)?.list
181
- ? $slashItems.map((i: any) => i.list).flat()[index]
182
- : $slashItems[index];
183
- if (item) {
184
- let range = $slashProps.range;
185
- item.command({ editor: tiptap.v, range });
186
- }
212
+ const item = flattenSlashItems()[index];
213
+ const { editor, range } = slashState.props;
214
+ if (!item || !editor || !range) return;
215
+ item.command({ editor, range });
187
216
  }
188
217
  </script>
189
218
 
@@ -204,7 +233,7 @@
204
233
  {/if}
205
234
  </div>
206
235
  {#if editable}
207
- <Command {selectedIndex} />
236
+ <Command />
208
237
  <Floating />
209
238
  {/if}
210
239
  {#if editable || mark}
@@ -214,112 +243,163 @@
214
243
  {/if}
215
244
  </main>
216
245
 
217
- <style>main {
218
- position: relative;
219
- overscroll-behavior: none;
220
- --shadow: 0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
221
- 0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
222
- 0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
223
- }
224
- main.fullscreen {
225
- z-index: 999999999;
226
- position: fixed;
227
- top: 0;
228
- left: 0;
229
- right: 0;
230
- bottom: 0;
231
- background: var(--surface);
232
- padding: 82px 12px 12px 12px;
233
- }
234
- main .wrapper {
235
- position: relative;
236
- }
237
-
238
- .target > :global(div) > :global(*:first-child) {
239
- margin-top: 0 !important;
240
- }
241
- .target > :global(div) > :global(*:last-child) {
242
- margin-bottom: 0 !important;
243
- }
244
-
245
- .editable :global(.ProseMirror-selectednode img) {
246
- transition: all 0.2s ease-in-out;
247
- filter: drop-shadow(0 0 0.75rem var(--primary-light13));
248
- }
249
-
250
- .editable :global(.iframe-wrapper.ProseMirror-selectednode) {
251
- outline: 3px solid var(--primary);
252
- }
253
-
254
- .editable :global(lite-youtube.ProseMirror-selectednode) {
255
- outline: 3px solid var(--primary);
256
- }
257
-
258
- div > :global(div) {
259
- outline: none !important;
260
- }
261
- div > :global(div) :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
262
- color: var(--on-surface, #000);
263
- opacity: 0.7;
264
- content: attr(data-placeholder);
265
- float: left;
266
- height: 0;
267
- pointer-events: none;
268
- transition: 0.2s opacity ease-in-out;
269
- }
270
- div > :global(div) :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
271
- opacity: 0;
272
- }
273
- div > :global(div) :global(a) {
274
- cursor: pointer;
275
- }
276
- div > :global(div) :global(img) {
277
- transition: all 0.2s ease-in-out;
278
- max-width: 100%;
279
- border-radius: 12px;
280
- position: relative;
281
- }
282
- div > :global(div) :global(code.inline) {
283
- background: var(--primary-light1);
284
- padding: 2px 4px;
285
- border-radius: 4px;
286
- }
287
- div > :global(div) :global(pre) {
288
- background: var(--primary-light1);
289
- padding: 12px;
290
- border-radius: 12px;
291
- max-width: 100%;
292
- }
293
- div > :global(div) :global(table) {
294
- border-collapse: collapse;
295
- width: 100%;
296
- margin: 8px 0;
297
- border: 1px solid var(--primary-light1);
298
- border-radius: 12px;
299
- }
300
- div > :global(div) :global(th),
301
- div > :global(div) :global(td) {
302
- padding: 8px;
303
- border: 1px solid var(--primary-light1);
304
- }
305
- div > :global(div) :global(.math-render) {
306
- cursor: initial;
307
- }
308
- div > :global(div) :global(lite-youtube) {
309
- border-radius: 12px;
310
- }
311
- div > :global(div) :global(.iframe-wrapper) {
312
- position: relative;
313
- padding-bottom: 12px;
314
- overflow: hidden;
315
- width: 100%;
316
- height: 600px;
317
- border-radius: 12px;
318
- }
319
- div > :global(div) :global(iframe) {
320
- position: absolute;
321
- top: 0;
322
- left: 0;
323
- width: 100%;
324
- height: 100%;
325
- }</style>
246
+ <style>
247
+ main {
248
+ position: relative;
249
+ overscroll-behavior: none;
250
+ --shadow:
251
+ 0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
252
+ 0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
253
+ 0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
254
+ &.fullscreen {
255
+ z-index: 999999999;
256
+ position: fixed;
257
+ top: 0;
258
+ left: 0;
259
+ right: 0;
260
+ bottom: 0;
261
+ background: var(--surface);
262
+ padding: 82px 12px 12px 12px;
263
+ }
264
+
265
+ & .wrapper {
266
+ position: relative;
267
+ }
268
+ }
269
+
270
+ main :global(.tiptap-code-block-toolbar) {
271
+ position: absolute;
272
+ top: 8px;
273
+ right: 8px;
274
+ z-index: 1;
275
+ display: flex;
276
+ justify-content: flex-end;
277
+ }
278
+
279
+ main:not(.editable) :global(.tiptap-code-block-toolbar) {
280
+ visibility: hidden;
281
+ pointer-events: none;
282
+ }
283
+
284
+ .target > :global(div) > :global(*:first-child) {
285
+ margin-top: 0 !important;
286
+ }
287
+
288
+ .target > :global(div) > :global(*:last-child) {
289
+ margin-bottom: 0 !important;
290
+ }
291
+
292
+ .editable :global(.ProseMirror-selectednode img) {
293
+ transition: all 0.2s ease-in-out;
294
+ filter: drop-shadow(0 0 0.75rem var(--primary-light13));
295
+ }
296
+
297
+ .editable :global(.iframe-wrapper.ProseMirror-selectednode) {
298
+ outline: 3px solid var(--primary);
299
+ }
300
+
301
+ .editable :global(lite-youtube.ProseMirror-selectednode) {
302
+ outline: 3px solid var(--primary);
303
+ }
304
+
305
+ div > :global(div) {
306
+ outline: none !important;
307
+ & :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
308
+ color: var(--on-surface, #000);
309
+ opacity: 0.7;
310
+ content: attr(data-placeholder);
311
+ float: left;
312
+ height: 0;
313
+ pointer-events: none;
314
+ transition: 0.2s opacity ease-in-out;
315
+ }
316
+
317
+ & :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
318
+ opacity: 0;
319
+ }
320
+
321
+ & :global(a) {
322
+ cursor: pointer;
323
+ }
324
+
325
+ & :global(img) {
326
+ transition: all 0.2s ease-in-out;
327
+ max-width: 100%;
328
+ border-radius: 12px;
329
+ position: relative;
330
+ }
331
+
332
+ & :global(code.inline) {
333
+ background: var(--primary-light1);
334
+ padding: 2px 4px;
335
+ border-radius: 4px;
336
+ }
337
+
338
+ & :global(pre) {
339
+ background: var(--primary-light1);
340
+ padding: 12px;
341
+ border-radius: 12px;
342
+ max-width: 100%;
343
+ }
344
+
345
+ & :global(.tiptap-code-block) {
346
+ max-width: 100%;
347
+ position: relative;
348
+ }
349
+
350
+ & :global(.tiptap-code-block-language) {
351
+ padding: 2px 6px;
352
+ border-radius: 8px;
353
+ border: 1px solid var(--primary-light2, #ddd);
354
+ outline: 1px solid transparent;
355
+ outline-offset: 0;
356
+ background: var(--surface, #fff);
357
+ color: var(--on-surface, #000);
358
+ font-size: 0.75em;
359
+
360
+ &:focus,
361
+ &:focus-visible {
362
+ outline-color: var(--primary-light9, #89a2d9);
363
+ }
364
+ }
365
+
366
+ & :global(table) {
367
+ border-collapse: collapse;
368
+ width: 100%;
369
+ margin: 8px 0;
370
+ border: 1px solid var(--primary-light1);
371
+ border-radius: 12px;
372
+ }
373
+
374
+ & :global(th),
375
+ & :global(td) {
376
+ padding: 8px;
377
+ border: 1px solid var(--primary-light1);
378
+ }
379
+
380
+ & :global(.math-render) {
381
+ cursor: initial;
382
+ }
383
+
384
+ & :global(lite-youtube) {
385
+ border-radius: 12px;
386
+ }
387
+
388
+ & :global(.iframe-wrapper) {
389
+ position: relative;
390
+ padding-bottom: 12px;
391
+ overflow: hidden;
392
+ width: 100%;
393
+ height: 600px;
394
+ border-radius: 12px;
395
+ }
396
+
397
+ & :global(iframe) {
398
+ position: absolute;
399
+ top: 0;
400
+ left: 0;
401
+ width: 100%;
402
+ height: 100%;
403
+ }
404
+ }
405
+ </style>
@@ -1,6 +1,6 @@
1
1
  import '@seorii/prosemirror-math/style.css';
2
2
  import type { UploadFn } from '../plugin/image/dragdrop';
3
- declare const TipTap: import("svelte").Component<{
3
+ type Props = {
4
4
  body: string;
5
5
  editable?: boolean;
6
6
  mark?: boolean;
@@ -11,10 +11,14 @@ declare const TipTap: import("svelte").Component<{
11
11
  style?: string;
12
12
  blocks?: any[];
13
13
  placeholder?: string;
14
+ locale?: string;
14
15
  sanitize?: Record<string, any>;
15
16
  colors?: string[];
16
17
  bubble?: any;
17
18
  preloader?: any;
18
- crossorigin?: "anonymous" | "use-credentials";
19
- }, {}, "body" | "ref" | "loaded">;
19
+ crossorigin?: 'anonymous' | 'use-credentials';
20
+ codeBlockLanguageLabels?: Record<string, string>;
21
+ };
22
+ declare const TipTap: import("svelte").Component<Props, {}, "body" | "ref" | "loaded">;
23
+ type TipTap = ReturnType<typeof TipTap>;
20
24
  export default TipTap;
@@ -2,36 +2,56 @@
2
2
  import { getContext } from 'svelte';
3
3
  import { Button, IconButton, Render } from 'nunui';
4
4
 
5
- const editor = getContext<{ v: any }>('editor');
5
+ const editor = getContext<{ v: any; c: number }>('editor');
6
6
  const tiptap = $derived(editor.v);
7
7
 
8
+ type Props = {
9
+ prop?: string;
10
+ attrs?: Record<string, unknown>;
11
+ label?: string;
12
+ icon?: string;
13
+ methodName?: string;
14
+ tooltip?: string | Record<string, any>;
15
+ handler?: (() => void) | null;
16
+ children?: any;
17
+ [key: string]: unknown;
18
+ };
19
+
8
20
  let {
9
21
  prop = '',
10
- attrs = '',
22
+ attrs = {},
11
23
  label = '',
12
24
  icon = '',
13
25
  methodName = 'toggle' + prop.charAt(0).toUpperCase() + prop.slice(1),
14
26
  tooltip,
15
- handler,
27
+ handler = null,
28
+ children,
16
29
  ...rest
17
- } = $props();
30
+ }: Props = $props();
18
31
 
19
- const isActive = $derived(() => {
20
- return editor && prop && tiptap.isActive(prop, attrs);
32
+ const isActive = $derived.by(() => {
33
+ // Recompute active styles on every editor transaction (selection/mark changes)
34
+ editor.c;
35
+ return !!(prop && tiptap?.isActive(prop, attrs));
21
36
  });
22
37
 
23
38
  function toggle() {
24
39
  if (!tiptap) return;
25
- //tiptap.chain().focus().clearNodes().run()
26
40
  if (handler) return handler();
27
- setTimeout(() => tiptap.chain().focus()[methodName](attrs)?.run(), 0);
41
+ setTimeout(() => {
42
+ const chain = tiptap.chain().focus() as Record<
43
+ string,
44
+ (attrs?: unknown) => { run?: () => void }
45
+ >;
46
+ chain[methodName]?.(attrs)?.run?.();
47
+ }, 0);
28
48
  }
29
49
  </script>
30
50
 
31
51
  {#if icon}
32
- <IconButton size="1.2em" {icon} active={isActive()} onclick={toggle} {tooltip} tabindex="0" />
52
+ <IconButton size="1.2em" {icon} active={isActive} onclick={toggle} {tooltip} tabindex={0} />
33
53
  {:else}
34
- <Button outlined={!isActive()} onclick={handler || toggle} small {...rest}>
54
+ <Button outlined={!isActive} onclick={handler || toggle} small {...rest}>
35
55
  {label}
36
56
  <Render it={children} />
37
57
  </Button>
@@ -1,10 +1,14 @@
1
- declare const ToolbarButton: import("svelte").Component<{
1
+ type Props = {
2
2
  prop?: string;
3
- attrs?: string;
3
+ attrs?: Record<string, unknown>;
4
4
  label?: string;
5
5
  icon?: string;
6
- methodName?: unknown;
7
- tooltip: unknown;
8
- handler: unknown;
9
- } & Record<string, unknown>, {}, "">;
6
+ methodName?: string;
7
+ tooltip?: string | Record<string, any>;
8
+ handler?: (() => void) | null;
9
+ children?: any;
10
+ [key: string]: unknown;
11
+ };
12
+ declare const ToolbarButton: import("svelte").Component<Props, {}, "">;
13
+ type ToolbarButton = ReturnType<typeof ToolbarButton>;
10
14
  export default ToolbarButton;
@@ -1 +1,2 @@
1
- export default function setMath(tiptap: any): void;
1
+ import type { Editor } from '@tiptap/core';
2
+ export default function setMath(tiptap: Editor): void;
@@ -1,17 +1,79 @@
1
+ import { NodeSelection } from '@tiptap/pm/state';
2
+ const pushUniqueTarget = (targets, from, to, text) => {
3
+ if (targets.some((target) => target.from === from && target.to === to))
4
+ return;
5
+ targets.push({ from, to, text });
6
+ };
7
+ const collectMathTargets = (state, mathInline) => {
8
+ const targets = [];
9
+ const { selection } = state;
10
+ if (selection instanceof NodeSelection && selection.node.type === mathInline) {
11
+ pushUniqueTarget(targets, selection.from, selection.to, selection.node.textContent);
12
+ }
13
+ if (selection.$from.parent.type === mathInline) {
14
+ const depth = selection.$from.depth;
15
+ const from = selection.$from.before(depth);
16
+ const to = selection.$from.after(depth);
17
+ pushUniqueTarget(targets, from, to, selection.$from.parent.textContent);
18
+ }
19
+ state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
20
+ if (node.type !== mathInline)
21
+ return;
22
+ pushUniqueTarget(targets, position, position + node.nodeSize, node.textContent);
23
+ return false;
24
+ });
25
+ return targets;
26
+ };
27
+ const unwrapMath = ({ state, tr }, mathInline) => {
28
+ const targets = collectMathTargets(state, mathInline);
29
+ if (!targets.length)
30
+ return false;
31
+ for (const { from, to, text } of targets.sort((a, b) => b.from - a.from)) {
32
+ if (!text.length) {
33
+ tr.delete(from, to);
34
+ continue;
35
+ }
36
+ tr.replaceWith(from, to, state.schema.text(text));
37
+ }
38
+ return true;
39
+ };
40
+ const wrapSelectionAsMath = ({ state, tr }, mathInline) => {
41
+ const { selection } = state;
42
+ if (selection.empty)
43
+ return false;
44
+ const targets = [];
45
+ state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
46
+ if (!node.isTextblock)
47
+ return;
48
+ const contentFrom = position + 1;
49
+ const contentTo = position + node.nodeSize - 1;
50
+ const from = Math.max(selection.from, contentFrom);
51
+ const to = Math.min(selection.to, contentTo);
52
+ if (from >= to)
53
+ return;
54
+ const text = state.doc.textBetween(from, to, '');
55
+ if (!text.length)
56
+ return;
57
+ targets.push({ from, to, text });
58
+ });
59
+ if (!targets.length)
60
+ return false;
61
+ for (const { from, to, text } of targets.sort((a, b) => b.from - a.from)) {
62
+ const newNode = mathInline.create(null, state.schema.text(text));
63
+ tr.replaceWith(from, to, newNode);
64
+ }
65
+ return true;
66
+ };
1
67
  export default function setMath(tiptap) {
2
- const { selection } = tiptap.state;
3
68
  tiptap
4
69
  .chain()
5
- .command(({ state, tr }) => state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
6
- if (!node.isTextblock || selection.from === selection.to)
7
- return;
8
- const startPosition = Math.max(position + 1, selection.from);
9
- const endPosition = Math.min(position + node.nodeSize, selection.to);
10
- const substringFrom = Math.max(0, selection.from - position - 1);
11
- const substringTo = Math.max(0, selection.to - position - 1);
12
- const updatedText = node.textContent.substring(substringFrom, substringTo);
13
- const newNode = state.schema.nodes.math_inline.create(null, state.schema.text(updatedText));
14
- tr = tr.replaceWith(startPosition, endPosition, newNode);
15
- }))
70
+ .command((props) => {
71
+ const mathInline = props.state.schema.nodes.math_inline;
72
+ if (!mathInline || props.state.selection.empty)
73
+ return false;
74
+ if (unwrapMath(props, mathInline))
75
+ return true;
76
+ return wrapSelectionAsMath(props, mathInline);
77
+ })
16
78
  .run();
17
79
  }
@@ -1,3 +1,11 @@
1
1
  import { Editor } from '@tiptap/core';
2
- declare const _default: (element: Element, content: string, { placeholder, plugins, crossorigin, ...props }?: any) => Editor;
2
+ type CodeBlockLanguageLabelMap = Record<string, string>;
3
+ type CrossOrigin = 'anonymous' | 'use-credentials' | undefined;
4
+ declare const _default: (element: Element, content: string, { placeholder, plugins, crossorigin, codeBlockLanguageLabels, ...props }?: {
5
+ placeholder?: string;
6
+ plugins?: any[];
7
+ crossorigin?: CrossOrigin;
8
+ codeBlockLanguageLabels?: CodeBlockLanguageLabelMap;
9
+ [key: string]: unknown;
10
+ }) => Editor;
3
11
  export default _default;