@teriusu/rich-editor 0.7.0 → 0.7.2

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 (33) hide show
  1. package/dist/components/BubbleToolbar.svelte +169 -118
  2. package/dist/components/BubbleToolbar.svelte.d.ts.map +1 -1
  3. package/dist/components/FixedToolbar.svelte +410 -814
  4. package/dist/components/FixedToolbar.svelte.d.ts +0 -1
  5. package/dist/components/FixedToolbar.svelte.d.ts.map +1 -1
  6. package/dist/components/SlashCommandMenu.svelte +28 -25
  7. package/dist/components/SlashCommandMenu.svelte.d.ts +1 -1
  8. package/dist/components/SlashCommandMenu.svelte.d.ts.map +1 -1
  9. package/dist/components/TipTapEditor.svelte +2 -64
  10. package/dist/components/TipTapEditor.svelte.d.ts.map +1 -1
  11. package/dist/extensions/Columns.d.ts.map +1 -1
  12. package/dist/extensions/Columns.js +35 -0
  13. package/dist/extensions/FixedDetails.d.ts.map +1 -1
  14. package/dist/extensions/FixedDetails.js +11 -0
  15. package/dist/extensions/MbusVideo.d.ts.map +1 -1
  16. package/dist/extensions/MbusVideo.js +15 -41
  17. package/dist/index.d.ts +0 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +0 -1
  20. package/dist/styles/editor.css +1 -1
  21. package/dist/types.d.ts +1 -1
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/types.js +8 -7
  24. package/dist/utils/sanitize.d.ts.map +1 -1
  25. package/dist/utils/sanitize.js +0 -3
  26. package/dist/utils/sanitize.test.js +0 -7
  27. package/package.json +1 -2
  28. package/dist/extensions/VideoBlock.d.ts +0 -16
  29. package/dist/extensions/VideoBlock.d.ts.map +0 -1
  30. package/dist/extensions/VideoBlock.js +0 -87
  31. package/dist/extensions/VideoBlock.test.d.ts +0 -2
  32. package/dist/extensions/VideoBlock.test.d.ts.map +0 -1
  33. package/dist/extensions/VideoBlock.test.js +0 -54
package/dist/types.js CHANGED
@@ -4,27 +4,28 @@ export const TOOLBAR_PRESETS = {
4
4
  'bold', 'italic', 'underline', 'strike', 'highlight',
5
5
  'superscript', 'subscript', 'code', 'text-color',
6
6
  'align-left', 'align-center', 'align-right',
7
- 'h1', 'h2', 'h3',
7
+ 'paragraph', 'h1', 'h2', 'h3',
8
8
  'bullet-list', 'ordered-list', 'checklist',
9
9
  'blockquote', 'horizontal-rule', 'toggle',
10
- 'link', 'image', 'pdf', 'youtube', 'video', 'file', 'mbus',
10
+ 'link', 'image', 'pdf', 'file', 'mbus',
11
11
  'columns-2', 'columns-3', 'table', 'code-block',
12
12
  'undo', 'redo',
13
13
  'fixed-toolbar', 'slash-menu',
14
14
  'table-menu', 'character-count', 'upload-overlay'
15
15
  ],
16
16
  standard: [
17
- 'bold', 'italic', 'strike', 'code', 'text-color',
18
- 'h1', 'h2', 'h3',
17
+ 'bold', 'italic', 'underline', 'strike', 'code', 'text-color',
18
+ 'align-left', 'align-center', 'align-right',
19
+ 'paragraph', 'h1', 'h2', 'h3',
19
20
  'bullet-list', 'ordered-list', 'checklist',
20
21
  'blockquote', 'horizontal-rule', 'toggle',
21
- 'link', 'image', 'pdf', 'youtube', 'video', 'file', 'mbus',
22
+ 'link', 'image', 'pdf', 'file', 'mbus',
22
23
  'columns-2', 'columns-3', 'table', 'code-block',
23
24
  'fixed-toolbar', 'slash-menu', 'table-menu'
24
25
  ],
25
26
  minimal: [
26
- 'bold', 'italic', 'strike', 'code',
27
- 'h2', 'h3',
27
+ 'bold', 'italic', 'underline', 'strike', 'code',
28
+ 'paragraph', 'h2', 'h3',
28
29
  'bullet-list', 'ordered-list', 'checklist',
29
30
  'blockquote', 'code-block',
30
31
  'link', 'image', 'file', 'pdf',
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AA4BA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcjD;AA0BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA4DxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAWrE"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AA4BA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcjD;AA0BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsDxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAWrE"}
@@ -70,9 +70,6 @@ export function transformLegacyHtml(html) {
70
70
  // <tiptap-collapsable title="X">content</tiptap-collapsable>
71
71
  // → <details><summary>X</summary><div>content</div></details>
72
72
  .replace(/<tiptap-collapsable\s+title="([^"]*)">([\s\S]*?)<\/tiptap-collapsable>/gi, '<details><summary>$1</summary><div>$2</div></details>')
73
- // <lite-youtube videoid="X" ...></lite-youtube>
74
- // → <div data-youtube-video><iframe src="https://www.youtube.com/embed/X" allowfullscreen></iframe></div>
75
- .replace(/<lite-youtube\s+videoid="([^"]*)"[^>]*>(?:<\/lite-youtube>)?/gi, '<div data-youtube-video=""><iframe src="https://www.youtube.com/embed/$1" allowfullscreen></iframe></div>')
76
73
  // <embed src="X" type="application/pdf" ...>
77
74
  // → <div data-pdf-src="X" data-pdf-name="filename">
78
75
  .replace(/<embed\s+[^>]*src="([^"]*)"[^>]*type="application\/pdf"[^>]*\/?>/gi, (_match, src) => {
@@ -36,13 +36,6 @@ describe('transformLegacyHtml', () => {
36
36
  expect(result).toContain('Hidden content');
37
37
  expect(result).not.toContain('tiptap-collapsable');
38
38
  });
39
- it('converts <lite-youtube videoid="X"> to YouTube iframe', () => {
40
- const input = '<lite-youtube videoid="dQw4w9WgXcQ" params=""></lite-youtube>';
41
- const result = transformLegacyHtml(input);
42
- expect(result).toContain('https://www.youtube.com/embed/dQw4w9WgXcQ');
43
- expect(result).toContain('data-youtube-video');
44
- expect(result).not.toContain('lite-youtube');
45
- });
46
39
  it('converts <embed type="application/pdf"> (src before type)', () => {
47
40
  const input = '<embed src="https://example.com/file.pdf" type="application/pdf" />';
48
41
  const result = transformLegacyHtml(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teriusu/rich-editor",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "TipTap 기반 Notion-style 리치 텍스트 에디터 (Svelte)",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",
@@ -55,7 +55,6 @@
55
55
  "@tiptap/extension-text-style": "^3.20.4",
56
56
  "@tiptap/extension-typography": "^3.21.0",
57
57
  "@tiptap/extension-underline": "^3.20.4",
58
- "@tiptap/extension-youtube": "^3.21.0",
59
58
  "@tiptap/pm": "^3.20.4",
60
59
  "@tiptap/starter-kit": "^3.22.0",
61
60
  "clsx": "^2.1.1",
@@ -1,16 +0,0 @@
1
- import { Node } from "@tiptap/core";
2
- export interface VideoBlockOptions {
3
- HTMLAttributes: Record<string, unknown>;
4
- }
5
- declare module "@tiptap/core" {
6
- interface Commands<ReturnType> {
7
- videoBlock: {
8
- setVideoBlock: (attrs: {
9
- src: string;
10
- name?: string;
11
- }) => ReturnType;
12
- };
13
- }
14
- }
15
- export declare const VideoBlock: Node<VideoBlockOptions, any>;
16
- //# sourceMappingURL=VideoBlock.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VideoBlock.d.ts","sourceRoot":"","sources":["../../src/extensions/VideoBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,UAAU,EAAE;YACV,aAAa,EAAE,CAAC,KAAK,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,KAAK,UAAU,CAAC;SACtE,CAAC;KACH;CACF;AAED,eAAO,MAAM,UAAU,8BAkGrB,CAAC"}
@@ -1,87 +0,0 @@
1
- import { Node, mergeAttributes } from "@tiptap/core";
2
- export const VideoBlock = Node.create({
3
- name: "videoBlock",
4
- group: "block",
5
- atom: true,
6
- draggable: true,
7
- addOptions() {
8
- return { HTMLAttributes: {} };
9
- },
10
- addAttributes() {
11
- return {
12
- src: { default: null },
13
- name: { default: "" },
14
- };
15
- },
16
- parseHTML() {
17
- return [
18
- {
19
- tag: 'div[data-video-src]',
20
- getAttrs: (dom) => {
21
- const el = dom;
22
- return {
23
- src: el.getAttribute("data-video-src"),
24
- name: el.getAttribute("data-video-name") || "",
25
- };
26
- },
27
- },
28
- ];
29
- },
30
- renderHTML({ HTMLAttributes }) {
31
- return [
32
- "div",
33
- mergeAttributes(this.options.HTMLAttributes, {
34
- "data-video-src": HTMLAttributes.src,
35
- "data-video-name": HTMLAttributes.name || "",
36
- }),
37
- ];
38
- },
39
- addNodeView() {
40
- return ({ node, editor }) => {
41
- const dom = document.createElement("div");
42
- dom.style.cssText = "margin:8px 0;position:relative;";
43
- const video = document.createElement("video");
44
- video.src = node.attrs.src;
45
- video.controls = true;
46
- video.preload = "metadata";
47
- video.style.cssText =
48
- "width:100%;max-height:480px;border-radius:8px;background:#0b1020;";
49
- video.setAttribute("controlslist", "nodownload");
50
- video.setAttribute("playsinline", "");
51
- dom.appendChild(video);
52
- if (node.attrs.name) {
53
- const label = document.createElement("p");
54
- label.textContent = node.attrs.name;
55
- label.style.cssText =
56
- "margin:4px 0 0;font-size:12px;color:var(--muted-foreground, #718096);";
57
- dom.appendChild(label);
58
- }
59
- if (editor.isEditable) {
60
- const del = document.createElement("button");
61
- del.type = "button";
62
- del.textContent = "\u00D7";
63
- del.style.cssText =
64
- "position:absolute;top:8px;right:8px;width:28px;height:28px;border:none;background:rgba(0,0,0,0.6);color:#fff;font-size:18px;cursor:pointer;border-radius:50%;display:flex;align-items:center;justify-content:center;z-index:2;";
65
- del.addEventListener("mouseenter", () => {
66
- del.style.background = "var(--destructive, #FF6B6B)";
67
- });
68
- del.addEventListener("mouseleave", () => {
69
- del.style.background = "rgba(0,0,0,0.6)";
70
- });
71
- del.addEventListener("click", () => {
72
- const pos = editor.view.posAtDOM(dom, 0);
73
- editor.chain().focus().deleteRange({ from: pos, to: pos + 1 }).run();
74
- });
75
- dom.appendChild(del);
76
- }
77
- return { dom };
78
- };
79
- },
80
- addCommands() {
81
- return {
82
- setVideoBlock: (attrs) => ({ chain }) => {
83
- return chain().insertContent({ type: this.name, attrs }).run();
84
- },
85
- };
86
- },
87
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=VideoBlock.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VideoBlock.test.d.ts","sourceRoot":"","sources":["../../src/extensions/VideoBlock.test.ts"],"names":[],"mappings":""}
@@ -1,54 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import { Editor } from '@tiptap/core';
3
- import StarterKit from '@tiptap/starter-kit';
4
- import { VideoBlock } from './VideoBlock';
5
- describe('VideoBlock extension', () => {
6
- let editor;
7
- function createEditor(content = '<p></p>') {
8
- editor = new Editor({
9
- element: document.createElement('div'),
10
- extensions: [StarterKit, VideoBlock],
11
- content
12
- });
13
- return editor;
14
- }
15
- afterEach(() => {
16
- editor?.destroy();
17
- });
18
- it('registers with name "videoBlock"', () => {
19
- createEditor();
20
- const ext = editor.extensionManager.extensions.find((e) => e.name === 'videoBlock');
21
- expect(ext).toBeDefined();
22
- });
23
- it('parses <div data-video-src="url">', () => {
24
- createEditor('<div data-video-src="https://example.com/video.mp4" data-video-name="demo"></div>');
25
- const doc = editor.getJSON();
26
- const videoNode = doc.content?.find((n) => n.type === 'videoBlock');
27
- expect(videoNode).toBeDefined();
28
- expect(videoNode?.attrs?.src).toBe('https://example.com/video.mp4');
29
- expect(videoNode?.attrs?.name).toBe('demo');
30
- });
31
- it('renders correct HTML', () => {
32
- createEditor('<div data-video-src="https://example.com/video.mp4" data-video-name="demo"></div>');
33
- const html = editor.getHTML();
34
- expect(html).toContain('data-video-src="https://example.com/video.mp4"');
35
- expect(html).toContain('data-video-name="demo"');
36
- });
37
- it('defaults name to empty string', () => {
38
- createEditor('<div data-video-src="https://example.com/video.mp4"></div>');
39
- const doc = editor.getJSON();
40
- const videoNode = doc.content?.find((n) => n.type === 'videoBlock');
41
- expect(videoNode?.attrs?.name).toBe('');
42
- });
43
- it('inserts via setVideoBlock command', () => {
44
- createEditor();
45
- editor.commands.setVideoBlock({
46
- src: 'https://example.com/v.mp4',
47
- name: 'My Video'
48
- });
49
- const doc = editor.getJSON();
50
- const videoNode = doc.content?.find((n) => n.type === 'videoBlock');
51
- expect(videoNode).toBeDefined();
52
- expect(videoNode?.attrs?.src).toBe('https://example.com/v.mp4');
53
- });
54
- });