@seorii/tiptap 0.3.0-next.9 → 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 +104 -70
  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 +210 -133
  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 || {}),
@@ -89,11 +105,16 @@
89
105
 
90
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,14 +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
139
  onTransaction: () => {
118
140
  tiptap.v = ref = tiptap.v;
119
141
  tiptap.c++;
120
142
  },
121
143
  crossorigin,
144
+ codeBlockLanguageLabels,
122
145
  ...options
123
146
  });
124
147
  tiptap.v.on('update', ({ editor: tiptap }: any) => {
@@ -150,29 +173,35 @@
150
173
  tiptap.v?.commands?.setContent?.(body);
151
174
  });
152
175
 
153
- let selectedIndex = $state(0);
154
- $effect(() => {
155
- if (!slashVisible) selectedIndex = 0;
156
- });
157
-
158
176
  function handleKeydown(event: KeyboardEvent) {
159
- if (!$slashVisible) return;
160
- let count = $slashItems.length;
161
- 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
+
162
189
  if (event.key === 'ArrowUp') {
163
190
  event.preventDefault();
164
- selectedIndex = (selectedIndex + count - 1) % count;
191
+ slashState.selectedIndex -= 1;
192
+ normalizeSlashIndex();
165
193
  return true;
166
194
  }
167
195
  if (event.key === 'ArrowDown') {
168
196
  event.preventDefault();
169
- selectedIndex = (selectedIndex + 1) % count;
197
+ slashState.selectedIndex += 1;
198
+ normalizeSlashIndex();
170
199
  return true;
171
200
  }
172
201
 
173
202
  if (event.key === 'Enter') {
174
203
  event.preventDefault();
175
- selectItem(selectedIndex);
204
+ selectItem(slashState.selectedIndex);
176
205
  return true;
177
206
  }
178
207
 
@@ -180,13 +209,10 @@
180
209
  }
181
210
 
182
211
  function selectItem(index: number) {
183
- const item = ($slashItems[0] as any)?.list
184
- ? $slashItems.map((i: any) => i.list).flat()[index]
185
- : $slashItems[index];
186
- if (item) {
187
- let range = $slashProps.range;
188
- item.command({ editor: tiptap.v, range });
189
- }
212
+ const item = flattenSlashItems()[index];
213
+ const { editor, range } = slashState.props;
214
+ if (!item || !editor || !range) return;
215
+ item.command({ editor, range });
190
216
  }
191
217
  </script>
192
218
 
@@ -207,7 +233,7 @@
207
233
  {/if}
208
234
  </div>
209
235
  {#if editable}
210
- <Command {selectedIndex} />
236
+ <Command />
211
237
  <Floating />
212
238
  {/if}
213
239
  {#if editable || mark}
@@ -217,112 +243,163 @@
217
243
  {/if}
218
244
  </main>
219
245
 
220
- <style>main {
221
- position: relative;
222
- overscroll-behavior: none;
223
- --shadow: 0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
224
- 0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
225
- 0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
226
- }
227
- main.fullscreen {
228
- z-index: 999999999;
229
- position: fixed;
230
- top: 0;
231
- left: 0;
232
- right: 0;
233
- bottom: 0;
234
- background: var(--surface);
235
- padding: 82px 12px 12px 12px;
236
- }
237
- main .wrapper {
238
- position: relative;
239
- }
240
-
241
- .target > :global(div) > :global(*:first-child) {
242
- margin-top: 0 !important;
243
- }
244
- .target > :global(div) > :global(*:last-child) {
245
- margin-bottom: 0 !important;
246
- }
247
-
248
- .editable :global(.ProseMirror-selectednode img) {
249
- transition: all 0.2s ease-in-out;
250
- filter: drop-shadow(0 0 0.75rem var(--primary-light13));
251
- }
252
-
253
- .editable :global(.iframe-wrapper.ProseMirror-selectednode) {
254
- outline: 3px solid var(--primary);
255
- }
256
-
257
- .editable :global(lite-youtube.ProseMirror-selectednode) {
258
- outline: 3px solid var(--primary);
259
- }
260
-
261
- div > :global(div) {
262
- outline: none !important;
263
- }
264
- div > :global(div) :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
265
- color: var(--on-surface, #000);
266
- opacity: 0.7;
267
- content: attr(data-placeholder);
268
- float: left;
269
- height: 0;
270
- pointer-events: none;
271
- transition: 0.2s opacity ease-in-out;
272
- }
273
- div > :global(div) :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
274
- opacity: 0;
275
- }
276
- div > :global(div) :global(a) {
277
- cursor: pointer;
278
- }
279
- div > :global(div) :global(img) {
280
- transition: all 0.2s ease-in-out;
281
- max-width: 100%;
282
- border-radius: 12px;
283
- position: relative;
284
- }
285
- div > :global(div) :global(code.inline) {
286
- background: var(--primary-light1);
287
- padding: 2px 4px;
288
- border-radius: 4px;
289
- }
290
- div > :global(div) :global(pre) {
291
- background: var(--primary-light1);
292
- padding: 12px;
293
- border-radius: 12px;
294
- max-width: 100%;
295
- }
296
- div > :global(div) :global(table) {
297
- border-collapse: collapse;
298
- width: 100%;
299
- margin: 8px 0;
300
- border: 1px solid var(--primary-light1);
301
- border-radius: 12px;
302
- }
303
- div > :global(div) :global(th),
304
- div > :global(div) :global(td) {
305
- padding: 8px;
306
- border: 1px solid var(--primary-light1);
307
- }
308
- div > :global(div) :global(.math-render) {
309
- cursor: initial;
310
- }
311
- div > :global(div) :global(lite-youtube) {
312
- border-radius: 12px;
313
- }
314
- div > :global(div) :global(.iframe-wrapper) {
315
- position: relative;
316
- padding-bottom: 12px;
317
- overflow: hidden;
318
- width: 100%;
319
- height: 600px;
320
- border-radius: 12px;
321
- }
322
- div > :global(div) :global(iframe) {
323
- position: absolute;
324
- top: 0;
325
- left: 0;
326
- width: 100%;
327
- height: 100%;
328
- }</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;