@seorii/tiptap 0.3.0-next.9 → 0.4.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 (55) hide show
  1. package/dist/i18n/index.d.ts +106 -6
  2. package/dist/i18n/index.js +56 -11
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +2 -1
  5. package/dist/plugin/command/emoji.d.ts +3 -17
  6. package/dist/plugin/command/emoji.js +51 -24
  7. package/dist/plugin/command/stores.svelte.d.ts +52 -0
  8. package/dist/plugin/command/stores.svelte.js +69 -0
  9. package/dist/plugin/command/suggest.d.ts +6 -19
  10. package/dist/plugin/command/suggest.js +149 -49
  11. package/dist/plugin/embed.d.ts +2 -2
  12. package/dist/plugin/embed.js +6 -2
  13. package/dist/plugin/iframe.js +1 -1
  14. package/dist/plugin/image/dragdrop.d.ts +2 -0
  15. package/dist/plugin/image/dragdrop.js +126 -15
  16. package/dist/plugin/image/index.js +4 -3
  17. package/dist/plugin/indent.js +0 -1
  18. package/dist/plugin/orderedlist/index.d.ts +1 -1
  19. package/dist/plugin/orderedlist/index.js +1 -1
  20. package/dist/plugin/orderedlist/{korean.scss → korean.css} +2 -2
  21. package/dist/plugin/resize/index.d.ts +8 -0
  22. package/dist/plugin/resize/index.js +454 -0
  23. package/dist/plugin/table/index.d.ts +1 -1
  24. package/dist/plugin/table/index.js +19 -11
  25. package/dist/plugin/table/style/{cell.scss → cell.css} +6 -5
  26. package/dist/plugin/table/style/{grip.scss → grip.css} +14 -19
  27. package/dist/plugin/table/style/resize.css +28 -0
  28. package/dist/plugin/table/style/{table.scss → table.css} +15 -17
  29. package/dist/plugin/table/style.css +4 -0
  30. package/dist/plugin/table/tableCell/index.js +2 -4
  31. package/dist/plugin/table/tableHeader/index.js +1 -2
  32. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte +97 -0
  33. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte.d.ts +5 -0
  34. package/dist/plugin/upload/skeleton/index.d.ts +30 -0
  35. package/dist/plugin/upload/skeleton/index.js +147 -0
  36. package/dist/plugin/youtube.js +1 -1
  37. package/dist/tiptap/Bubble.svelte +231 -92
  38. package/dist/tiptap/Bubble.svelte.d.ts +9 -6
  39. package/dist/tiptap/Command.svelte +160 -158
  40. package/dist/tiptap/Command.svelte.d.ts +2 -3
  41. package/dist/tiptap/Floating.svelte +51 -24
  42. package/dist/tiptap/Floating.svelte.d.ts +1 -0
  43. package/dist/tiptap/TipTap.svelte +302 -140
  44. package/dist/tiptap/TipTap.svelte.d.ts +10 -3
  45. package/dist/tiptap/ToolbarButton.svelte +30 -10
  46. package/dist/tiptap/ToolbarButton.svelte.d.ts +10 -6
  47. package/dist/tiptap/setMath.d.ts +2 -1
  48. package/dist/tiptap/setMath.js +74 -12
  49. package/dist/tiptap/tiptap.d.ts +9 -1
  50. package/dist/tiptap/tiptap.js +172 -16
  51. package/package.json +63 -57
  52. package/dist/plugin/command/stores.d.ts +0 -13
  53. package/dist/plugin/command/stores.js +0 -7
  54. package/dist/plugin/table/style/resize.scss +0 -26
  55. package/dist/plugin/table/style.scss +0 -4
@@ -1,5 +1,5 @@
1
1
  .ProseMirror {
2
- .tableWrapper {
2
+ & .tableWrapper {
3
3
  position: relative;
4
4
  margin-top: 0.75em;
5
5
  scrollbar-width: thin;
@@ -7,7 +7,7 @@
7
7
  transition: all 0.1s ease-in-out;
8
8
  }
9
9
 
10
- .scrollable {
10
+ & .scrollable {
11
11
  padding-left: 1em;
12
12
  margin-left: -1em;
13
13
  overflow: auto hidden;
@@ -16,7 +16,7 @@
16
16
  transition: border 250ms ease-in-out 0s;
17
17
  }
18
18
 
19
- .scrollable-shadow {
19
+ & .scrollable-shadow {
20
20
  position: absolute;
21
21
  top: 0;
22
22
  bottom: 0;
@@ -52,17 +52,17 @@
52
52
  }
53
53
  }
54
54
 
55
- table {
55
+ & table {
56
56
  margin-top: 1em;
57
57
  border-radius: 4px;
58
58
  border-collapse: collapse;
59
59
  box-sizing: border-box;
60
60
  width: 100% !important;
61
61
 
62
- h1,
63
- h2,
64
- h3,
65
- p {
62
+ & h1,
63
+ & h2,
64
+ & h3,
65
+ & p {
66
66
  margin: 0;
67
67
  }
68
68
 
@@ -72,15 +72,13 @@
72
72
  }
73
73
  }
74
74
 
75
- .editable {
76
- .ProseMirror {
77
- .tableWrapper {
78
- padding: 10px;
79
- }
75
+ .editable .ProseMirror {
76
+ & .tableWrapper {
77
+ padding: 10px;
78
+ }
80
79
 
81
- .selectedCell {
82
- border-style: double;
83
- background: var(--primary-light3);
84
- }
80
+ & .selectedCell {
81
+ border-style: double;
82
+ background: var(--primary-light3);
85
83
  }
86
84
  }
@@ -0,0 +1,4 @@
1
+ @import './style/grip.css';
2
+ @import './style/table.css';
3
+ @import './style/cell.css';
4
+ @import './style/resize.css';
@@ -18,14 +18,12 @@ export default BuiltInTableCell.extend({
18
18
  };
19
19
  },
20
20
  addProseMirrorPlugins() {
21
- const { isEditable } = this.editor;
22
21
  return [
23
22
  new Plugin({
24
23
  props: {
25
24
  decorations: (state) => {
26
- if (!isEditable) {
27
- //return DecorationSet.empty;
28
- }
25
+ if (!this.editor.isEditable)
26
+ return DecorationSet.empty;
29
27
  const { doc, selection } = state;
30
28
  const decorations = [];
31
29
  const cells = getCellsInColumn(0)(selection);
@@ -18,12 +18,11 @@ export default BuiltInTableHeader.extend({
18
18
  };
19
19
  },
20
20
  addProseMirrorPlugins() {
21
- const { isEditable } = this.editor;
22
21
  return [
23
22
  new Plugin({
24
23
  props: {
25
24
  decorations: (state) => {
26
- if (!isEditable)
25
+ if (!this.editor.isEditable)
27
26
  return DecorationSet.empty;
28
27
  const { doc, selection } = state;
29
28
  const decorations = [];
@@ -0,0 +1,97 @@
1
+ <script lang="ts">
2
+ import { NodeViewWrapper } from 'svelte-tiptap';
3
+ import type { NodeViewProps } from '@tiptap/core';
4
+
5
+ type UploadSkeletonProps = Pick<NodeViewProps, 'node'>;
6
+ let { node }: UploadSkeletonProps = $props();
7
+
8
+ const kind = $derived.by(() => {
9
+ const raw = node.attrs?.kind;
10
+ return typeof raw === 'string' && raw.trim().length ? raw : 'block';
11
+ });
12
+ const frameHeight = $derived.by(() => {
13
+ const raw = Number.parseFloat(String(node.attrs?.height ?? ''));
14
+ const base = Number.isFinite(raw) ? raw : 180;
15
+ return Math.max(44, Math.min(1200, Math.round(base)));
16
+ });
17
+ </script>
18
+
19
+ <NodeViewWrapper>
20
+ <div class="wrapper" data-kind={kind} style={`height: ${frameHeight}px`}>
21
+ <div class="shine"></div>
22
+ <div class="bar top"></div>
23
+ <div class="bar mid"></div>
24
+ <div class="bar low"></div>
25
+ </div>
26
+ </NodeViewWrapper>
27
+
28
+ <style>
29
+ .wrapper {
30
+ position: relative;
31
+ width: 100%;
32
+ overflow: hidden;
33
+ border-radius: 12px;
34
+ border: 1px solid var(--primary-light3, rgba(90, 90, 90, 0.25));
35
+ background: linear-gradient(
36
+ 135deg,
37
+ var(--surface2, rgba(120, 120, 120, 0.1)),
38
+ var(--surface3, rgba(80, 80, 80, 0.2))
39
+ );
40
+ }
41
+
42
+ .wrapper[data-kind='file'] {
43
+ min-height: 56px;
44
+ }
45
+
46
+ .shine {
47
+ position: absolute;
48
+ inset: 0;
49
+ transform: translateX(-120%);
50
+ background: linear-gradient(105deg, transparent 20%, rgba(255, 255, 255, 0.36), transparent 80%);
51
+ animation: shimmer 1.35s ease-in-out infinite;
52
+ }
53
+
54
+ .bar {
55
+ position: absolute;
56
+ left: 14px;
57
+ right: 14px;
58
+ height: 10px;
59
+ border-radius: 999px;
60
+ background: rgba(255, 255, 255, 0.42);
61
+ }
62
+
63
+ .top {
64
+ top: 14px;
65
+ width: min(160px, 45%);
66
+ }
67
+
68
+ .mid {
69
+ top: 34px;
70
+ width: min(280px, 78%);
71
+ }
72
+
73
+ .low {
74
+ top: 54px;
75
+ width: min(220px, 62%);
76
+ }
77
+
78
+ .wrapper[data-kind='file'] .mid,
79
+ .wrapper[data-kind='file'] .low {
80
+ display: none;
81
+ }
82
+
83
+ .wrapper[data-kind='image'] .bar,
84
+ .wrapper[data-kind='pdf'] .bar,
85
+ .wrapper[data-kind='embed'] .bar {
86
+ opacity: 0.78;
87
+ }
88
+
89
+ @keyframes shimmer {
90
+ from {
91
+ transform: translateX(-120%);
92
+ }
93
+ to {
94
+ transform: translateX(120%);
95
+ }
96
+ }
97
+ </style>
@@ -0,0 +1,5 @@
1
+ import type { NodeViewProps } from '@tiptap/core';
2
+ type UploadSkeletonProps = Pick<NodeViewProps, 'node'>;
3
+ declare const UploadSkeleton: import("svelte").Component<UploadSkeletonProps, {}, "">;
4
+ type UploadSkeleton = ReturnType<typeof UploadSkeleton>;
5
+ export default UploadSkeleton;
@@ -0,0 +1,30 @@
1
+ import { Node, type Editor, type JSONContent } from '@tiptap/core';
2
+ export declare const UPLOAD_SKELETON_NODE = "tiptap-upload-skeleton";
3
+ declare const defaultHeight: {
4
+ readonly image: 220;
5
+ readonly file: 56;
6
+ readonly pdf: 420;
7
+ readonly embed: 420;
8
+ readonly block: 180;
9
+ };
10
+ type UploadSkeletonKind = keyof typeof defaultHeight;
11
+ type InsertUploadSkeletonOptions = {
12
+ kind?: UploadSkeletonKind;
13
+ height?: number;
14
+ at?: number;
15
+ select?: boolean;
16
+ insertParagraph?: boolean;
17
+ };
18
+ type ReplaceOptions = {
19
+ select?: boolean;
20
+ };
21
+ type EditorLike = Pick<Editor, 'state' | 'view'>;
22
+ export type UploadSkeletonHandle = {
23
+ id: string;
24
+ exists: () => boolean;
25
+ replaceWith: (content: JSONContent, options?: ReplaceOptions) => boolean;
26
+ remove: () => boolean;
27
+ };
28
+ export declare function insertUploadSkeleton(editor: EditorLike, { kind, height, at, select, insertParagraph }?: InsertUploadSkeletonOptions): UploadSkeletonHandle | null;
29
+ declare const _default: Node<any, any>;
30
+ export default _default;
@@ -0,0 +1,147 @@
1
+ import { Node, mergeAttributes } from '@tiptap/core';
2
+ import { NodeSelection } from '@tiptap/pm/state';
3
+ import { SvelteNodeViewRenderer } from 'svelte-tiptap';
4
+ import UploadSkeleton from './UploadSkeleton.svelte';
5
+ export const UPLOAD_SKELETON_NODE = 'tiptap-upload-skeleton';
6
+ const defaultHeight = {
7
+ image: 220,
8
+ file: 56,
9
+ pdf: 420,
10
+ embed: 420,
11
+ block: 180
12
+ };
13
+ function findUploadSkeleton(doc, id) {
14
+ let foundPos = null;
15
+ let foundNode = null;
16
+ doc.descendants((node, pos) => {
17
+ if (node.type.name !== UPLOAD_SKELETON_NODE)
18
+ return;
19
+ if (node.attrs.uploadId !== id)
20
+ return;
21
+ foundPos = pos;
22
+ foundNode = node;
23
+ return false;
24
+ });
25
+ if (foundPos === null || foundNode === null)
26
+ return null;
27
+ return { pos: foundPos, node: foundNode };
28
+ }
29
+ function tryCreateNodeSelection(doc, pos) {
30
+ if (pos < 0 || pos > doc.content.size)
31
+ return null;
32
+ const node = doc.nodeAt(pos);
33
+ if (!node || node.type.spec.selectable === false)
34
+ return null;
35
+ try {
36
+ return NodeSelection.create(doc, pos);
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ export function insertUploadSkeleton(editor, { kind = 'block', height = defaultHeight[kind], at, select = true, insertParagraph = true } = {}) {
43
+ const skeletonType = editor.state.schema.nodes[UPLOAD_SKELETON_NODE];
44
+ if (!skeletonType)
45
+ return null;
46
+ const clampedHeight = Math.max(44, Math.min(1200, Math.round(height)));
47
+ const uploadId = `upload-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
48
+ const node = skeletonType.create({ uploadId, kind, height: clampedHeight });
49
+ const paragraph = editor.state.schema.nodes.paragraph?.create();
50
+ const safePos = Math.max(0, Math.min(at ?? editor.state.selection.from, editor.state.doc.content.size));
51
+ const tr = editor.state.tr.insert(safePos, node);
52
+ if (insertParagraph && paragraph) {
53
+ tr.insert(safePos + node.nodeSize, paragraph);
54
+ }
55
+ if (select) {
56
+ const nodeSelection = tryCreateNodeSelection(tr.doc, safePos);
57
+ if (nodeSelection)
58
+ tr.setSelection(nodeSelection);
59
+ }
60
+ editor.view.dispatch(tr);
61
+ return {
62
+ id: uploadId,
63
+ exists: () => Boolean(findUploadSkeleton(editor.state.doc, uploadId)),
64
+ replaceWith: (content, options = {}) => {
65
+ const target = findUploadSkeleton(editor.state.doc, uploadId);
66
+ if (!target)
67
+ return false;
68
+ if (!content?.type)
69
+ return false;
70
+ let nextNode;
71
+ try {
72
+ nextNode = editor.state.schema.nodeFromJSON(content);
73
+ }
74
+ catch {
75
+ return false;
76
+ }
77
+ const tr = editor.state.tr.replaceWith(target.pos, target.pos + target.node.nodeSize, nextNode);
78
+ if (options.select ?? true) {
79
+ const nodeSelection = tryCreateNodeSelection(tr.doc, target.pos);
80
+ if (nodeSelection)
81
+ tr.setSelection(nodeSelection);
82
+ }
83
+ editor.view.dispatch(tr);
84
+ return true;
85
+ },
86
+ remove: () => {
87
+ const target = findUploadSkeleton(editor.state.doc, uploadId);
88
+ if (!target)
89
+ return false;
90
+ const removeFrom = target.pos;
91
+ let removeTo = target.pos + target.node.nodeSize;
92
+ const nextNode = editor.state.doc.nodeAt(removeTo);
93
+ if (nextNode?.type.name === 'paragraph' && nextNode.content.size === 0) {
94
+ removeTo += nextNode.nodeSize;
95
+ }
96
+ editor.view.dispatch(editor.state.tr.deleteRange(removeFrom, removeTo));
97
+ return true;
98
+ }
99
+ };
100
+ }
101
+ export default Node.create({
102
+ name: UPLOAD_SKELETON_NODE,
103
+ group: 'block',
104
+ atom: true,
105
+ draggable: false,
106
+ selectable: true,
107
+ addAttributes() {
108
+ return {
109
+ uploadId: {
110
+ default: null,
111
+ parseHTML: (element) => element.getAttribute('data-upload-id'),
112
+ renderHTML: (attributes) => attributes.uploadId ? { 'data-upload-id': attributes.uploadId } : {}
113
+ },
114
+ kind: {
115
+ default: 'block',
116
+ parseHTML: (element) => element.getAttribute('data-upload-kind') || 'block',
117
+ renderHTML: (attributes) => attributes.kind ? { 'data-upload-kind': attributes.kind } : {}
118
+ },
119
+ height: {
120
+ default: defaultHeight.block,
121
+ parseHTML: (element) => {
122
+ const value = Number.parseInt(element.getAttribute('data-upload-height') || '', 10);
123
+ return Number.isFinite(value) ? value : defaultHeight.block;
124
+ },
125
+ renderHTML: (attributes) => {
126
+ const raw = Number.parseFloat(String(attributes.height ?? defaultHeight.block));
127
+ const height = Number.isFinite(raw) ? Math.max(44, Math.min(1200, Math.round(raw))) : 180;
128
+ return { 'data-upload-height': String(height) };
129
+ }
130
+ }
131
+ };
132
+ },
133
+ parseHTML() {
134
+ return [{ tag: UPLOAD_SKELETON_NODE }];
135
+ },
136
+ renderHTML({ HTMLAttributes }) {
137
+ return [
138
+ UPLOAD_SKELETON_NODE,
139
+ mergeAttributes(HTMLAttributes, {
140
+ 'data-bubble-menu': 'false'
141
+ })
142
+ ];
143
+ },
144
+ addNodeView() {
145
+ return SvelteNodeViewRenderer(UploadSkeleton);
146
+ }
147
+ });
@@ -5,7 +5,7 @@ const youtubeExtractId = (url) => {
5
5
  const match = youtubeRegExp.exec(url.trim());
6
6
  return match ? match[1] : false;
7
7
  };
8
- const videoPlayerStaticAttributes = { nocookie: true };
8
+ const videoPlayerStaticAttributes = { nocookie: true, 'data-bubble-menu': 'false' };
9
9
  export default Node.create({
10
10
  name: 'lite-youtube',
11
11
  content: '',