Skip to content

Commit f094e00

Browse files
committed
feat: 하이브리드 파일 저장, 컬럼 확장, 레거시 HTML 호환 (v0.4.0)
- FileAttachment: data-file-id + data-file-src 하이브리드 지원 - PdfBlock: data-pdf-id + data-pdf-src 하이브리드 지원 - onResolveFile prop: 호스트 앱이 파일 ID → URL 변환 제공 - Columns/Column 확장: 2단/3단 컬럼 레이아웃 - Indent: px 단위 파싱 추가 (em 출력 유지) - transformLegacyHtml: tiptap-file, tiptap-midibus, collapsable, lite-youtube, embed pdf, columns 변환 - 레거시 <tiptap-file> parseHTML 지원 - 레거시 <embed type="application/pdf"> parseHTML 지원
1 parent 39694dc commit f094e00

45 files changed

Lines changed: 2346 additions & 97 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dist/components/FixedToolbar.svelte

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
ChevronRight,
4040
Paperclip,
4141
Film,
42+
Columns2,
43+
Columns3,
4244
} from "lucide-svelte";
4345
import { cn } from "../utils/cn";
4446
import InputModal from "./InputModal.svelte";
@@ -462,6 +464,31 @@
462464
</button>
463465
{/if}
464466

467+
<!-- Columns -->
468+
<button
469+
type="button"
470+
onclick={() => editor.chain().focus().setColumns(2).run()}
471+
data-tooltip="2단 컬럼"
472+
aria-label="2단 컬럼"
473+
class={cn(
474+
"p-1.5 rounded-md transition-colors",
475+
editor.isActive("columns")
476+
? "bg-primary/10 text-primary"
477+
: "text-muted-foreground hover:bg-muted hover:text-foreground",
478+
)}
479+
>
480+
<Columns2 size={iconSize} />
481+
</button>
482+
<button
483+
type="button"
484+
onclick={() => editor.chain().focus().setColumns(3).run()}
485+
data-tooltip="3단 컬럼"
486+
aria-label="3단 컬럼"
487+
class="p-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground"
488+
>
489+
<Columns3 size={iconSize} />
490+
</button>
491+
465492
<!-- Table menu -->
466493
<div bind:this={tableMenuEl} class="relative">
467494
<button

dist/components/FixedToolbar.svelte.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/components/SlashCommandMenu.svelte

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
ChevronRight,
1919
Paperclip,
2020
Film,
21+
Columns2,
22+
Columns3,
2123
} from "lucide-svelte";
2224
import type { SlashMenuItem } from "../types";
2325
import type { Component } from "svelte";
@@ -112,6 +114,18 @@
112114
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
113115
.run(),
114116
},
117+
{
118+
label: "2단 컬럼",
119+
keywords: "column 컬럼 2단 분할",
120+
icon: Columns2,
121+
command: (editor) => editor.chain().focus().setColumns(2).run(),
122+
},
123+
{
124+
label: "3단 컬럼",
125+
keywords: "column 컬럼 3단 분할",
126+
icon: Columns3,
127+
command: (editor) => editor.chain().focus().setColumns(3).run(),
128+
},
115129
{
116130
label: "YouTube 영상",
117131
keywords: "youtube video 영상 동영상 유튜브",

dist/components/SlashCommandMenu.svelte.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/components/TipTapEditor.svelte

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
import { Extension, type AnyExtension } from "@tiptap/core";
3232
import { TextSelection } from "@tiptap/pm/state";
3333
import { PdfBlock } from "../extensions/PdfBlock";
34+
import { Columns } from "../extensions/Columns";
35+
import { Column } from "../extensions/Column";
36+
import { transformLegacyHtml } from "../utils/sanitize";
3437
import { Indent } from "../extensions/Indent";
3538
import { FileAttachment } from "../extensions/FileAttachment";
3639
import { VideoBlock } from "../extensions/VideoBlock";
@@ -39,6 +42,7 @@
3942
import SlashCommandMenu from "./SlashCommandMenu.svelte";
4043
import TableBubbleMenu from "./TableBubbleMenu.svelte";
4144
import type { UploadHandler } from "../types";
45+
import type { FileResolver } from "../extensions/FileAttachment";
4246
4347
const cellAttrs = {
4448
backgroundColor: {
@@ -107,13 +111,15 @@
107111
onChange,
108112
placeholder = "'/'를 눌러 명령어를 입력하세요...",
109113
onUploadFile,
114+
onResolveFile,
110115
extensions: extraExtensions = [],
111116
editable = true,
112117
}: {
113118
content: string;
114119
onChange: (html: string) => void;
115120
placeholder?: string;
116121
onUploadFile?: UploadHandler;
122+
onResolveFile?: FileResolver;
117123
extensions?: AnyExtension[];
118124
editable?: boolean;
119125
} = $props();
@@ -345,6 +351,8 @@
345351
PdfBlock,
346352
FileAttachment,
347353
VideoBlock,
354+
Columns,
355+
Column,
348356
CodeBlockTopEscape,
349357
Indent,
350358
FixedDetails,
@@ -444,7 +452,7 @@
444452
]
445453
: []),
446454
],
447-
content,
455+
content: transformLegacyHtml(content),
448456
editable,
449457
onUpdate: ({ editor: e }) => {
450458
const html = e
@@ -467,6 +475,11 @@
467475
},
468476
});
469477
478+
// File resolver를 storage에 등록
479+
if (onResolveFile) {
480+
editor.storage.fileAttachment = { resolver: onResolveFile };
481+
}
482+
470483
editor.on("update", handleUpdate);
471484
editor.on("selectionUpdate", handleSelectionUpdate);
472485
@@ -511,7 +524,8 @@
511524
if (!editor) return;
512525
// 에디터 자체 onChange에서 나온 값이면 무시 (무한 루프 방지)
513526
if (content === lastEmittedHtml) return;
514-
editor.commands.setContent(content, { emitUpdate: false });
527+
const transformed = transformLegacyHtml(content);
528+
editor.commands.setContent(transformed, { emitUpdate: false });
515529
lastEmittedHtml = content;
516530
editor.commands.fixTables();
517531
});

dist/components/TipTapEditor.svelte.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { type AnyExtension } from "@tiptap/core";
22
import type { UploadHandler } from "../types";
3+
import type { FileResolver } from "../extensions/FileAttachment";
34
type $$ComponentProps = {
45
content: string;
56
onChange: (html: string) => void;
67
placeholder?: string;
78
onUploadFile?: UploadHandler;
9+
onResolveFile?: FileResolver;
810
extensions?: AnyExtension[];
911
editable?: boolean;
1012
};

dist/components/TipTapEditor.svelte.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/extensions/Column.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Node as TiptapNode } from "@tiptap/core";
2+
export declare const Column: TiptapNode<any, any>;
3+
//# sourceMappingURL=Column.d.ts.map

dist/extensions/Column.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/extensions/Column.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Node as TiptapNode, mergeAttributes } from "@tiptap/core";
2+
export const Column = TiptapNode.create({
3+
name: "column",
4+
group: "",
5+
content: "block+",
6+
isolating: true,
7+
parseHTML() {
8+
return [
9+
{ tag: 'div[data-type="column"]' },
10+
{ tag: "div.tiptap-column" },
11+
];
12+
},
13+
renderHTML({ HTMLAttributes }) {
14+
return [
15+
"div",
16+
mergeAttributes(HTMLAttributes, { "data-type": "column" }),
17+
0,
18+
];
19+
},
20+
});

0 commit comments

Comments
 (0)