@teriusu/rich-editor 0.6.4 → 0.7.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.
- package/dist/components/BubbleToolbar.svelte +74 -0
- package/dist/components/BubbleToolbar.svelte.d.ts.map +1 -1
- package/dist/components/FixedToolbar.svelte +128 -4
- package/dist/components/FixedToolbar.svelte.d.ts +1 -0
- package/dist/components/FixedToolbar.svelte.d.ts.map +1 -1
- package/dist/components/SlashCommandMenu.svelte +1 -1
- package/dist/components/TipTapEditor.svelte +11 -9
- package/dist/components/TipTapEditor.svelte.d.ts +2 -0
- package/dist/components/TipTapEditor.svelte.d.ts.map +1 -1
- package/dist/extensions/FileAttachment.d.ts +5 -3
- package/dist/extensions/FileAttachment.d.ts.map +1 -1
- package/dist/extensions/FileAttachment.js +8 -12
- package/dist/extensions/Math.d.ts +28 -0
- package/dist/extensions/Math.d.ts.map +1 -0
- package/dist/extensions/Math.js +98 -0
- package/dist/extensions/MbusVideo.d.ts +16 -0
- package/dist/extensions/MbusVideo.d.ts.map +1 -0
- package/dist/extensions/MbusVideo.js +185 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles/editor.css +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/utils/sanitize.d.ts.map +1 -1
- package/dist/utils/sanitize.js +1 -0
- package/package.json +3 -1
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
Heading3,
|
|
21
21
|
Type,
|
|
22
22
|
Code,
|
|
23
|
+
Palette,
|
|
23
24
|
} from "lucide-svelte";
|
|
24
25
|
import { cn } from "../utils/cn";
|
|
25
26
|
import type { ToolbarFeature, PromptHandler } from "../types";
|
|
@@ -37,9 +38,22 @@
|
|
|
37
38
|
const has = (f: ToolbarFeature) => features.has(f);
|
|
38
39
|
|
|
39
40
|
let showHeadings = $state(false);
|
|
41
|
+
let showColors = $state(false);
|
|
40
42
|
let menuEl: HTMLDivElement | undefined = $state();
|
|
41
43
|
const iconSize = 14;
|
|
42
44
|
|
|
45
|
+
const TEXT_COLORS = [
|
|
46
|
+
{ label: "기본", value: "" },
|
|
47
|
+
{ label: "검정", value: "#000000" },
|
|
48
|
+
{ label: "회색", value: "#6b7280" },
|
|
49
|
+
{ label: "빨강", value: "#dc2626" },
|
|
50
|
+
{ label: "주황", value: "#ea580c" },
|
|
51
|
+
{ label: "노랑", value: "#ca8a04" },
|
|
52
|
+
{ label: "초록", value: "#16a34a" },
|
|
53
|
+
{ label: "파랑", value: "#2563eb" },
|
|
54
|
+
{ label: "보라", value: "#7c3aed" }
|
|
55
|
+
];
|
|
56
|
+
|
|
43
57
|
function getCurrentBlockLabel(): string {
|
|
44
58
|
if (editor.isActive("heading", { level: 1 })) return "제목 1";
|
|
45
59
|
if (editor.isActive("heading", { level: 2 })) return "제목 2";
|
|
@@ -221,6 +235,66 @@
|
|
|
221
235
|
<Strikethrough size={iconSize} />
|
|
222
236
|
</button>
|
|
223
237
|
{/if}
|
|
238
|
+
{#if has('text-color')}
|
|
239
|
+
<div class="relative">
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
onclick={() => (showColors = !showColors)}
|
|
243
|
+
title="글자색"
|
|
244
|
+
aria-label="글자색"
|
|
245
|
+
class={cn(
|
|
246
|
+
"p-1.5 rounded-full transition-colors",
|
|
247
|
+
editor.getAttributes("textStyle").color
|
|
248
|
+
? "bg-white/20 text-white"
|
|
249
|
+
: "text-white/70 hover:text-white hover:bg-white/10",
|
|
250
|
+
)}
|
|
251
|
+
>
|
|
252
|
+
<Palette size={iconSize} />
|
|
253
|
+
</button>
|
|
254
|
+
{#if showColors}
|
|
255
|
+
<div
|
|
256
|
+
class="absolute bottom-full left-0 mb-1 bg-foreground rounded-lg shadow-xl border border-white/10 p-2"
|
|
257
|
+
style="min-width: 160px"
|
|
258
|
+
>
|
|
259
|
+
<div class="grid grid-cols-3 gap-1.5">
|
|
260
|
+
{#each TEXT_COLORS as c}
|
|
261
|
+
<button
|
|
262
|
+
type="button"
|
|
263
|
+
title={c.label}
|
|
264
|
+
class="h-7 rounded-md border border-white/20 transition-transform hover:scale-105 flex items-center justify-center text-xs font-bold bg-white"
|
|
265
|
+
style="color: {c.value || '#000'}"
|
|
266
|
+
onclick={() => {
|
|
267
|
+
if (c.value) {
|
|
268
|
+
editor.chain().focus().setColor(c.value).run();
|
|
269
|
+
} else {
|
|
270
|
+
editor.chain().focus().unsetColor().run();
|
|
271
|
+
}
|
|
272
|
+
showColors = false;
|
|
273
|
+
}}
|
|
274
|
+
>
|
|
275
|
+
{c.value ? "A" : "×"}
|
|
276
|
+
</button>
|
|
277
|
+
{/each}
|
|
278
|
+
</div>
|
|
279
|
+
<label
|
|
280
|
+
class="mt-2 flex items-center justify-between gap-2 px-1 text-xs text-white/70 cursor-pointer hover:text-white"
|
|
281
|
+
>
|
|
282
|
+
<span>직접 선택</span>
|
|
283
|
+
<input
|
|
284
|
+
type="color"
|
|
285
|
+
class="h-6 w-10 cursor-pointer rounded border border-white/20 bg-transparent p-0"
|
|
286
|
+
value={(editor.getAttributes("textStyle").color as string) || "#000000"}
|
|
287
|
+
onclick={(e) => e.stopPropagation()}
|
|
288
|
+
oninput={(e) => {
|
|
289
|
+
const v = (e.target as HTMLInputElement).value;
|
|
290
|
+
editor.chain().focus().setColor(v).run();
|
|
291
|
+
}}
|
|
292
|
+
/>
|
|
293
|
+
</label>
|
|
294
|
+
</div>
|
|
295
|
+
{/if}
|
|
296
|
+
</div>
|
|
297
|
+
{/if}
|
|
224
298
|
{#if has('highlight')}
|
|
225
299
|
<button
|
|
226
300
|
type="button"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BubbleToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/BubbleToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"BubbleToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/BubbleToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAsB3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAkSJ,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
Paperclip,
|
|
41
41
|
Film,
|
|
42
42
|
Columns2,
|
|
43
|
+
Tv,
|
|
44
|
+
Palette,
|
|
43
45
|
} from "lucide-svelte";
|
|
44
46
|
import { cn } from "../utils/cn";
|
|
45
47
|
import InputModal from "./InputModal.svelte";
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
onVideoClick,
|
|
54
56
|
onPromptLink,
|
|
55
57
|
onPromptImage,
|
|
58
|
+
onPromptMbus,
|
|
56
59
|
}: {
|
|
57
60
|
editor: Editor;
|
|
58
61
|
features: Set<ToolbarFeature>;
|
|
@@ -61,6 +64,7 @@
|
|
|
61
64
|
onVideoClick?: () => void;
|
|
62
65
|
onPromptLink?: PromptHandler;
|
|
63
66
|
onPromptImage?: PromptHandler;
|
|
67
|
+
onPromptMbus?: PromptHandler;
|
|
64
68
|
} = $props();
|
|
65
69
|
|
|
66
70
|
const has = (f: ToolbarFeature) => features.has(f);
|
|
@@ -69,22 +73,44 @@
|
|
|
69
73
|
|
|
70
74
|
let codeMenuOpen = $state(false);
|
|
71
75
|
let tableMenuOpen = $state(false);
|
|
72
|
-
let
|
|
76
|
+
let colorMenuOpen = $state(false);
|
|
77
|
+
let colorPickerActive = $state(false);
|
|
78
|
+
let modalState: { type: "link" | "image" | "cellBg" | "mbus" } | null = $state(null);
|
|
73
79
|
let codeMenuEl: HTMLDivElement | undefined = $state();
|
|
74
80
|
let tableMenuEl: HTMLDivElement | undefined = $state();
|
|
81
|
+
let colorMenuEl: HTMLDivElement | undefined = $state();
|
|
82
|
+
|
|
83
|
+
const TEXT_COLORS = [
|
|
84
|
+
{ label: "기본", value: "" },
|
|
85
|
+
{ label: "검정", value: "#000000" },
|
|
86
|
+
{ label: "회색", value: "#6b7280" },
|
|
87
|
+
{ label: "빨강", value: "#dc2626" },
|
|
88
|
+
{ label: "주황", value: "#ea580c" },
|
|
89
|
+
{ label: "노랑", value: "#ca8a04" },
|
|
90
|
+
{ label: "초록", value: "#16a34a" },
|
|
91
|
+
{ label: "파랑", value: "#2563eb" },
|
|
92
|
+
{ label: "보라", value: "#7c3aed" }
|
|
93
|
+
];
|
|
75
94
|
|
|
76
95
|
$effect(() => {
|
|
77
|
-
if (!codeMenuOpen && !tableMenuOpen) return;
|
|
96
|
+
if (!codeMenuOpen && !tableMenuOpen && !colorMenuOpen) return;
|
|
78
97
|
function handleClick(e: MouseEvent) {
|
|
79
98
|
if (codeMenuEl && !codeMenuEl.contains(e.target as Node))
|
|
80
99
|
codeMenuOpen = false;
|
|
81
100
|
if (tableMenuEl && !tableMenuEl.contains(e.target as Node))
|
|
82
101
|
tableMenuOpen = false;
|
|
102
|
+
if (
|
|
103
|
+
colorMenuEl &&
|
|
104
|
+
!colorMenuEl.contains(e.target as Node) &&
|
|
105
|
+
!colorPickerActive
|
|
106
|
+
)
|
|
107
|
+
colorMenuOpen = false;
|
|
83
108
|
}
|
|
84
109
|
function handleKeyDown(e: KeyboardEvent) {
|
|
85
110
|
if (e.key === "Escape") {
|
|
86
111
|
codeMenuOpen = false;
|
|
87
112
|
tableMenuOpen = false;
|
|
113
|
+
colorMenuOpen = false;
|
|
88
114
|
}
|
|
89
115
|
}
|
|
90
116
|
document.addEventListener("mousedown", handleClick);
|
|
@@ -265,6 +291,74 @@
|
|
|
265
291
|
<Subscript size={iconSize} />
|
|
266
292
|
</button>
|
|
267
293
|
{/if}
|
|
294
|
+
{#if has('text-color')}
|
|
295
|
+
<div bind:this={colorMenuEl} class="relative">
|
|
296
|
+
<button
|
|
297
|
+
type="button"
|
|
298
|
+
onclick={() => (colorMenuOpen = !colorMenuOpen)}
|
|
299
|
+
data-tooltip="글자색"
|
|
300
|
+
aria-label="글자색"
|
|
301
|
+
class={cn(
|
|
302
|
+
"flex items-center gap-0.5 p-1.5 rounded-md transition-colors",
|
|
303
|
+
editor.getAttributes("textStyle").color
|
|
304
|
+
? "bg-primary/10 text-primary"
|
|
305
|
+
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
306
|
+
)}
|
|
307
|
+
>
|
|
308
|
+
<Palette size={iconSize} />
|
|
309
|
+
<ChevronDown size={12} />
|
|
310
|
+
</button>
|
|
311
|
+
{#if colorMenuOpen}
|
|
312
|
+
<div
|
|
313
|
+
class="absolute top-full left-0 mt-1 bg-popover border border-border rounded-lg shadow-lg z-50 p-2"
|
|
314
|
+
style="min-width: 180px"
|
|
315
|
+
>
|
|
316
|
+
<div class="grid grid-cols-3 gap-1.5">
|
|
317
|
+
{#each TEXT_COLORS as c}
|
|
318
|
+
<button
|
|
319
|
+
type="button"
|
|
320
|
+
title={c.label}
|
|
321
|
+
class="h-8 rounded-md border border-border transition-transform hover:scale-105 flex items-center justify-center text-xs font-bold"
|
|
322
|
+
style="color: {c.value || '#000'}"
|
|
323
|
+
onclick={() => {
|
|
324
|
+
if (c.value) {
|
|
325
|
+
editor.chain().focus().setColor(c.value).run();
|
|
326
|
+
} else {
|
|
327
|
+
editor.chain().focus().unsetColor().run();
|
|
328
|
+
}
|
|
329
|
+
colorMenuOpen = false;
|
|
330
|
+
}}
|
|
331
|
+
>
|
|
332
|
+
{c.value ? "A" : "×"}
|
|
333
|
+
</button>
|
|
334
|
+
{/each}
|
|
335
|
+
</div>
|
|
336
|
+
<label
|
|
337
|
+
class="mt-2 flex items-center justify-between gap-2 px-1 text-xs text-muted-foreground cursor-pointer hover:text-foreground"
|
|
338
|
+
>
|
|
339
|
+
<span>직접 선택</span>
|
|
340
|
+
<input
|
|
341
|
+
type="color"
|
|
342
|
+
class="h-6 w-10 cursor-pointer rounded border border-border bg-transparent p-0"
|
|
343
|
+
value={(editor.getAttributes("textStyle").color as string) || "#000000"}
|
|
344
|
+
onpointerdown={() => (colorPickerActive = true)}
|
|
345
|
+
onclick={(e) => e.stopPropagation()}
|
|
346
|
+
oninput={(e) => {
|
|
347
|
+
const v = (e.target as HTMLInputElement).value;
|
|
348
|
+
editor.chain().focus().setColor(v).run();
|
|
349
|
+
}}
|
|
350
|
+
onchange={() => {
|
|
351
|
+
setTimeout(() => (colorPickerActive = false), 200);
|
|
352
|
+
}}
|
|
353
|
+
onblur={() => {
|
|
354
|
+
setTimeout(() => (colorPickerActive = false), 200);
|
|
355
|
+
}}
|
|
356
|
+
/>
|
|
357
|
+
</label>
|
|
358
|
+
</div>
|
|
359
|
+
{/if}
|
|
360
|
+
</div>
|
|
361
|
+
{/if}
|
|
268
362
|
|
|
269
363
|
</div>
|
|
270
364
|
|
|
@@ -476,7 +570,7 @@
|
|
|
476
570
|
</div>
|
|
477
571
|
{/if}
|
|
478
572
|
|
|
479
|
-
{#if has('link') || has('image') || has('pdf') || has('youtube') || has('video') || has('file') || has('columns-2') || has('columns-3') || has('table') || has('code-block')}
|
|
573
|
+
{#if has('link') || has('image') || has('pdf') || has('youtube') || has('video') || has('file') || has('mbus') || has('columns-2') || has('columns-3') || has('table') || has('code-block')}
|
|
480
574
|
<!-- Media & Layout -->
|
|
481
575
|
<div class="hce-toolbar-group">
|
|
482
576
|
{#if has('link')}
|
|
@@ -531,6 +625,25 @@
|
|
|
531
625
|
<Video size={iconSize} />
|
|
532
626
|
</button>
|
|
533
627
|
{/if}
|
|
628
|
+
{#if has('mbus')}
|
|
629
|
+
<button
|
|
630
|
+
type="button"
|
|
631
|
+
onclick={async () => {
|
|
632
|
+
if (onPromptMbus) {
|
|
633
|
+
const url = await onPromptMbus("");
|
|
634
|
+
if (!url) return;
|
|
635
|
+
editor.chain().focus().setMbusVideo({ src: url }).run();
|
|
636
|
+
} else {
|
|
637
|
+
modalState = { type: "mbus" };
|
|
638
|
+
}
|
|
639
|
+
}}
|
|
640
|
+
data-tooltip="미디버스 영상"
|
|
641
|
+
aria-label="미디버스 영상"
|
|
642
|
+
class="p-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
643
|
+
>
|
|
644
|
+
<Tv size={iconSize} />
|
|
645
|
+
</button>
|
|
646
|
+
{/if}
|
|
534
647
|
{#if has('video') && onVideoClick}
|
|
535
648
|
<button
|
|
536
649
|
type="button"
|
|
@@ -833,7 +946,7 @@
|
|
|
833
946
|
{#if has('code-block')}
|
|
834
947
|
<button
|
|
835
948
|
type="button"
|
|
836
|
-
onclick={() => editor.chain().focus().setCodeBlock(
|
|
949
|
+
onclick={() => editor.chain().focus().setCodeBlock().run()}
|
|
837
950
|
data-tooltip="코드 블록"
|
|
838
951
|
aria-label="코드 블록"
|
|
839
952
|
class={cn(
|
|
@@ -917,6 +1030,17 @@
|
|
|
917
1030
|
onCancel={() => (modalState = null)}
|
|
918
1031
|
/>
|
|
919
1032
|
{/if}
|
|
1033
|
+
{#if modalState?.type === "mbus"}
|
|
1034
|
+
<InputModal
|
|
1035
|
+
title="미디버스 영상 URL"
|
|
1036
|
+
placeholder="https://play.mbus.tv/v1/hls/..."
|
|
1037
|
+
onConfirm={(url) => {
|
|
1038
|
+
editor.chain().focus().setMbusVideo({ src: url }).run();
|
|
1039
|
+
modalState = null;
|
|
1040
|
+
}}
|
|
1041
|
+
onCancel={() => (modalState = null)}
|
|
1042
|
+
/>
|
|
1043
|
+
{/if}
|
|
920
1044
|
{#if modalState?.type === "cellBg"}
|
|
921
1045
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
922
1046
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
@@ -8,6 +8,7 @@ type $$ComponentProps = {
|
|
|
8
8
|
onVideoClick?: () => void;
|
|
9
9
|
onPromptLink?: PromptHandler;
|
|
10
10
|
onPromptImage?: PromptHandler;
|
|
11
|
+
onPromptMbus?: PromptHandler;
|
|
11
12
|
};
|
|
12
13
|
declare const FixedToolbar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
14
|
type FixedToolbar = ReturnType<typeof FixedToolbar>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FixedToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/FixedToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"FixedToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/FixedToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AA8C3C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAwsBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
import { Indent } from "../extensions/Indent";
|
|
38
38
|
import { FileAttachment } from "../extensions/FileAttachment";
|
|
39
39
|
import { VideoBlock } from "../extensions/VideoBlock";
|
|
40
|
+
import { MbusVideo } from "../extensions/MbusVideo";
|
|
40
41
|
import FixedToolbar from "./FixedToolbar.svelte";
|
|
41
42
|
import BubbleToolbar from "./BubbleToolbar.svelte";
|
|
42
43
|
import SlashCommandMenu from "./SlashCommandMenu.svelte";
|
|
@@ -113,8 +114,10 @@
|
|
|
113
114
|
placeholder = "'/'를 눌러 명령어를 입력하세요...",
|
|
114
115
|
onUploadFile,
|
|
115
116
|
onResolveFile,
|
|
117
|
+
fileDownloadBaseUrl,
|
|
116
118
|
onPromptLink,
|
|
117
119
|
onPromptImage,
|
|
120
|
+
onPromptMbus,
|
|
118
121
|
extensions: extraExtensions = [],
|
|
119
122
|
editable = true,
|
|
120
123
|
toolbar = 'full',
|
|
@@ -125,8 +128,10 @@
|
|
|
125
128
|
placeholder?: string;
|
|
126
129
|
onUploadFile?: UploadHandler;
|
|
127
130
|
onResolveFile?: FileResolver;
|
|
131
|
+
fileDownloadBaseUrl?: string;
|
|
128
132
|
onPromptLink?: PromptHandler;
|
|
129
133
|
onPromptImage?: PromptHandler;
|
|
134
|
+
onPromptMbus?: PromptHandler;
|
|
130
135
|
extensions?: AnyExtension[];
|
|
131
136
|
editable?: boolean;
|
|
132
137
|
toolbar?: ToolbarMode;
|
|
@@ -370,8 +375,12 @@
|
|
|
370
375
|
CustomTableHeader,
|
|
371
376
|
CustomTableCell,
|
|
372
377
|
PdfBlock,
|
|
373
|
-
FileAttachment
|
|
378
|
+
FileAttachment.configure({
|
|
379
|
+
resolver: onResolveFile ?? null,
|
|
380
|
+
...(fileDownloadBaseUrl ? { downloadBaseUrl: fileDownloadBaseUrl } : {}),
|
|
381
|
+
}),
|
|
374
382
|
VideoBlock,
|
|
383
|
+
MbusVideo,
|
|
375
384
|
Columns,
|
|
376
385
|
Column,
|
|
377
386
|
CodeBlockTopEscape,
|
|
@@ -496,14 +505,6 @@
|
|
|
496
505
|
},
|
|
497
506
|
});
|
|
498
507
|
|
|
499
|
-
// File resolver를 storage에 등록
|
|
500
|
-
if (onResolveFile) {
|
|
501
|
-
editor.storage.fileAttachment = {
|
|
502
|
-
...editor.storage.fileAttachment,
|
|
503
|
-
resolver: onResolveFile,
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
|
|
507
508
|
editor.on("update", handleUpdate);
|
|
508
509
|
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
509
510
|
|
|
@@ -575,6 +576,7 @@
|
|
|
575
576
|
{features}
|
|
576
577
|
{onPromptLink}
|
|
577
578
|
{onPromptImage}
|
|
579
|
+
{onPromptMbus}
|
|
578
580
|
onPdfClick={() => pdfInputEl?.click()}
|
|
579
581
|
onFileClick={onUploadFile ? () => fileInputEl?.click() : undefined}
|
|
580
582
|
onVideoClick={onUploadFile ? () => videoInputEl?.click() : undefined}
|
|
@@ -7,8 +7,10 @@ type $$ComponentProps = {
|
|
|
7
7
|
placeholder?: string;
|
|
8
8
|
onUploadFile?: UploadHandler;
|
|
9
9
|
onResolveFile?: FileResolver;
|
|
10
|
+
fileDownloadBaseUrl?: string;
|
|
10
11
|
onPromptLink?: PromptHandler;
|
|
11
12
|
onPromptImage?: PromptHandler;
|
|
13
|
+
onPromptMbus?: PromptHandler;
|
|
12
14
|
extensions?: AnyExtension[];
|
|
13
15
|
editable?: boolean;
|
|
14
16
|
toolbar?: ToolbarMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TipTapEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/components/TipTapEditor.svelte.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"TipTapEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/components/TipTapEditor.svelte.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAc5D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEhE,KAAK,gBAAgB,GAAI;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B,CAAC;AA8mBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Node } from "@tiptap/core";
|
|
2
|
-
export interface FileAttachmentOptions {
|
|
3
|
-
HTMLAttributes: Record<string, unknown>;
|
|
4
|
-
}
|
|
5
2
|
export type FileResolveResult = {
|
|
6
3
|
src: string;
|
|
7
4
|
name?: string;
|
|
8
5
|
size?: number;
|
|
9
6
|
};
|
|
10
7
|
export type FileResolver = (fileId: string) => Promise<FileResolveResult>;
|
|
8
|
+
export interface FileAttachmentOptions {
|
|
9
|
+
HTMLAttributes: Record<string, unknown>;
|
|
10
|
+
resolver: FileResolver | null;
|
|
11
|
+
downloadBaseUrl: string;
|
|
12
|
+
}
|
|
11
13
|
declare module "@tiptap/core" {
|
|
12
14
|
interface Commands<ReturnType> {
|
|
13
15
|
fileAttachment: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileAttachment.d.ts","sourceRoot":"","sources":["../../src/extensions/FileAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,
|
|
1
|
+
{"version":3,"file":"FileAttachment.d.ts","sourceRoot":"","sources":["../../src/extensions/FileAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,cAAc,EAAE;YACd,iBAAiB,EAAE,CAAC,KAAK,EAAE;gBACzB,GAAG,CAAC,EAAE,MAAM,CAAC;gBACb,MAAM,CAAC,EAAE,MAAM,CAAC;gBAChB,IAAI,EAAE,MAAM,CAAC;gBACb,IAAI,CAAC,EAAE,MAAM,CAAC;aACf,KAAK,UAAU,CAAC;SAClB,CAAC;KACH;CACF;AAkCD,eAAO,MAAM,cAAc,kCAiNzB,CAAC"}
|
|
@@ -8,7 +8,7 @@ function formatFileSize(bytes) {
|
|
|
8
8
|
}
|
|
9
9
|
function isInlineable(name) {
|
|
10
10
|
const ext = name.split(".").pop()?.toLowerCase() || "";
|
|
11
|
-
return ["pdf", "png", "jpg", "jpeg", "gif", "webp", "svg"].includes(ext);
|
|
11
|
+
return ["pdf", "png", "jpg", "jpeg", "gif", "webp", "svg", "txt", "html", "htm"].includes(ext);
|
|
12
12
|
}
|
|
13
13
|
function getFileIcon(name) {
|
|
14
14
|
const ext = name.split(".").pop()?.toLowerCase() || "";
|
|
@@ -36,7 +36,7 @@ export const FileAttachment = Node.create({
|
|
|
36
36
|
atom: true,
|
|
37
37
|
draggable: true,
|
|
38
38
|
addOptions() {
|
|
39
|
-
return { HTMLAttributes: {} };
|
|
39
|
+
return { HTMLAttributes: {}, resolver: null, downloadBaseUrl: "/api/upload" };
|
|
40
40
|
},
|
|
41
41
|
addAttributes() {
|
|
42
42
|
return {
|
|
@@ -168,15 +168,14 @@ export const FileAttachment = Node.create({
|
|
|
168
168
|
sizeEl.textContent = formatFileSize(node.attrs.size);
|
|
169
169
|
}
|
|
170
170
|
else if (node.attrs.fileId) {
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
sizeEl.textContent = "loading...";
|
|
171
|
+
// fileId만 있어도 proxy URL로 다운로드 가능 — 버튼은 항상 활성.
|
|
172
|
+
// resolver가 있으면 이름/크기 메타데이터를 채운다.
|
|
174
173
|
const resolver = editor.storage.fileAttachment?.resolver;
|
|
175
174
|
if (resolver) {
|
|
175
|
+
sizeEl.textContent = "loading...";
|
|
176
176
|
resolver(node.attrs.fileId)
|
|
177
177
|
.then((result) => {
|
|
178
178
|
resolvedSrc = result.src;
|
|
179
|
-
nameEl.disabled = false;
|
|
180
179
|
if (result.name) {
|
|
181
180
|
resolvedName = result.name;
|
|
182
181
|
nameEl.textContent = result.name;
|
|
@@ -185,12 +184,9 @@ export const FileAttachment = Node.create({
|
|
|
185
184
|
sizeEl.textContent = result.size ? formatFileSize(result.size) : "";
|
|
186
185
|
})
|
|
187
186
|
.catch(() => {
|
|
188
|
-
sizeEl.textContent = "
|
|
187
|
+
sizeEl.textContent = "";
|
|
189
188
|
});
|
|
190
189
|
}
|
|
191
|
-
else {
|
|
192
|
-
sizeEl.textContent = `ID: ${node.attrs.fileId}`;
|
|
193
|
-
}
|
|
194
190
|
}
|
|
195
191
|
dom.appendChild(icon);
|
|
196
192
|
dom.appendChild(nameEl);
|
|
@@ -220,8 +216,8 @@ export const FileAttachment = Node.create({
|
|
|
220
216
|
},
|
|
221
217
|
addStorage() {
|
|
222
218
|
return {
|
|
223
|
-
resolver:
|
|
224
|
-
downloadBaseUrl:
|
|
219
|
+
resolver: this.options.resolver,
|
|
220
|
+
downloadBaseUrl: this.options.downloadBaseUrl,
|
|
225
221
|
};
|
|
226
222
|
},
|
|
227
223
|
addCommands() {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KaTeX 기반 inline/display math 노드.
|
|
3
|
+
*
|
|
4
|
+
* 저장 포맷은 prod(arcturus + @seorii/prosemirror-math)와 호환:
|
|
5
|
+
* - inline: <math-inline class="math-node">LATEX</math-inline>
|
|
6
|
+
* - display: <math-display class="math-node">LATEX</math-display>
|
|
7
|
+
*
|
|
8
|
+
* 노드는 atom이며 텍스트 콘텐츠(LaTeX 원문)를 자식 텍스트 노드로 보관한다.
|
|
9
|
+
* NodeView가 KaTeX로 렌더링한다.
|
|
10
|
+
*/
|
|
11
|
+
import { Node } from "@tiptap/core";
|
|
12
|
+
interface MathOptions {
|
|
13
|
+
HTMLAttributes: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
declare module "@tiptap/core" {
|
|
16
|
+
interface Commands<ReturnType> {
|
|
17
|
+
mathInline: {
|
|
18
|
+
insertMathInline: (latex: string) => ReturnType;
|
|
19
|
+
};
|
|
20
|
+
mathDisplay: {
|
|
21
|
+
insertMathDisplay: (latex: string) => ReturnType;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export declare const MathInline: Node<MathOptions, any>;
|
|
26
|
+
export declare const MathDisplay: Node<MathOptions, any>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=Math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Math.d.ts","sourceRoot":"","sources":["../../src/extensions/Math.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAmB,IAAI,EAAE,MAAM,cAAc,CAAC;AAGrD,UAAU,WAAW;IACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,UAAU,EAAE;YACV,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC;SACjD,CAAC;QACF,WAAW,EAAE;YACX,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC;SAClD,CAAC;KACH;CACF;AA2BD,eAAO,MAAM,UAAU,wBAsCrB,CAAC;AAEH,eAAO,MAAM,WAAW,wBAuCtB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KaTeX 기반 inline/display math 노드.
|
|
3
|
+
*
|
|
4
|
+
* 저장 포맷은 prod(arcturus + @seorii/prosemirror-math)와 호환:
|
|
5
|
+
* - inline: <math-inline class="math-node">LATEX</math-inline>
|
|
6
|
+
* - display: <math-display class="math-node">LATEX</math-display>
|
|
7
|
+
*
|
|
8
|
+
* 노드는 atom이며 텍스트 콘텐츠(LaTeX 원문)를 자식 텍스트 노드로 보관한다.
|
|
9
|
+
* NodeView가 KaTeX로 렌더링한다.
|
|
10
|
+
*/
|
|
11
|
+
import { mergeAttributes, Node } from "@tiptap/core";
|
|
12
|
+
import katex from "katex";
|
|
13
|
+
function renderKatex(latex, displayMode) {
|
|
14
|
+
try {
|
|
15
|
+
return katex.renderToString(latex, {
|
|
16
|
+
displayMode,
|
|
17
|
+
throwOnError: false,
|
|
18
|
+
strict: false,
|
|
19
|
+
output: "html",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return `<span class="math-error">${latex}</span>`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function buildNodeView(displayMode) {
|
|
27
|
+
return ({ node, HTMLAttributes }) => {
|
|
28
|
+
const tag = displayMode ? "math-display" : "math-inline";
|
|
29
|
+
const dom = document.createElement(tag);
|
|
30
|
+
Object.entries({ class: "math-node", ...HTMLAttributes }).forEach(([k, v]) => {
|
|
31
|
+
if (v != null)
|
|
32
|
+
dom.setAttribute(k, String(v));
|
|
33
|
+
});
|
|
34
|
+
dom.innerHTML = renderKatex(node.textContent, displayMode);
|
|
35
|
+
return { dom };
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export const MathInline = Node.create({
|
|
39
|
+
name: "math_inline",
|
|
40
|
+
group: "inline",
|
|
41
|
+
inline: true,
|
|
42
|
+
atom: true,
|
|
43
|
+
content: "text*",
|
|
44
|
+
selectable: true,
|
|
45
|
+
addOptions() {
|
|
46
|
+
return {
|
|
47
|
+
HTMLAttributes: { class: "math-node" },
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
parseHTML() {
|
|
51
|
+
return [{ tag: "math-inline" }];
|
|
52
|
+
},
|
|
53
|
+
renderHTML({ HTMLAttributes }) {
|
|
54
|
+
return ["math-inline", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
55
|
+
},
|
|
56
|
+
addNodeView() {
|
|
57
|
+
return ({ node, HTMLAttributes }) => buildNodeView(false)({ node: node, HTMLAttributes });
|
|
58
|
+
},
|
|
59
|
+
addCommands() {
|
|
60
|
+
return {
|
|
61
|
+
insertMathInline: (latex) => ({ commands }) => commands.insertContent({
|
|
62
|
+
type: "math_inline",
|
|
63
|
+
content: [{ type: "text", text: latex }],
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
export const MathDisplay = Node.create({
|
|
69
|
+
name: "math_display",
|
|
70
|
+
group: "block",
|
|
71
|
+
atom: true,
|
|
72
|
+
content: "text*",
|
|
73
|
+
code: true,
|
|
74
|
+
selectable: true,
|
|
75
|
+
defining: true,
|
|
76
|
+
addOptions() {
|
|
77
|
+
return {
|
|
78
|
+
HTMLAttributes: { class: "math-node" },
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
parseHTML() {
|
|
82
|
+
return [{ tag: "math-display" }];
|
|
83
|
+
},
|
|
84
|
+
renderHTML({ HTMLAttributes }) {
|
|
85
|
+
return ["math-display", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
86
|
+
},
|
|
87
|
+
addNodeView() {
|
|
88
|
+
return ({ node, HTMLAttributes }) => buildNodeView(true)({ node: node, HTMLAttributes });
|
|
89
|
+
},
|
|
90
|
+
addCommands() {
|
|
91
|
+
return {
|
|
92
|
+
insertMathDisplay: (latex) => ({ commands }) => commands.insertContent({
|
|
93
|
+
type: "math_display",
|
|
94
|
+
content: [{ type: "text", text: latex }],
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Node } from "@tiptap/core";
|
|
2
|
+
export interface MbusVideoOptions {
|
|
3
|
+
HTMLAttributes: Record<string, unknown>;
|
|
4
|
+
}
|
|
5
|
+
declare module "@tiptap/core" {
|
|
6
|
+
interface Commands<ReturnType> {
|
|
7
|
+
mbusVideo: {
|
|
8
|
+
setMbusVideo: (attrs: {
|
|
9
|
+
src: string;
|
|
10
|
+
width?: string;
|
|
11
|
+
}) => ReturnType;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export declare const MbusVideo: Node<MbusVideoOptions, any>;
|
|
16
|
+
//# sourceMappingURL=MbusVideo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MbusVideo.d.ts","sourceRoot":"","sources":["../../src/extensions/MbusVideo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,OAAO,QAAQ,cAAc,CAAC;IAC7B,UAAU,QAAQ,CAAC,UAAU;QAC5B,SAAS,EAAE;YACV,YAAY,EAAE,CAAC,KAAK,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,KAAK,UAAU,CAAC;SACrE,CAAC;KACF;CACD;AAgCD,eAAO,MAAM,SAAS,6BAqKpB,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Node, mergeAttributes } from "@tiptap/core";
|
|
2
|
+
function attachHls(video, src) {
|
|
3
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
4
|
+
video.src = src;
|
|
5
|
+
return { destroy: () => { } };
|
|
6
|
+
}
|
|
7
|
+
let hls = null;
|
|
8
|
+
let cancelled = false;
|
|
9
|
+
import("hls.js")
|
|
10
|
+
.then(({ default: Hls }) => {
|
|
11
|
+
if (cancelled)
|
|
12
|
+
return;
|
|
13
|
+
if (!Hls.isSupported()) {
|
|
14
|
+
video.src = src;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const instance = new Hls();
|
|
18
|
+
instance.loadSource(src);
|
|
19
|
+
instance.attachMedia(video);
|
|
20
|
+
hls = instance;
|
|
21
|
+
})
|
|
22
|
+
.catch(() => {
|
|
23
|
+
if (!cancelled)
|
|
24
|
+
video.src = src;
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
destroy: () => {
|
|
28
|
+
cancelled = true;
|
|
29
|
+
hls?.destroy();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export const MbusVideo = Node.create({
|
|
34
|
+
name: "mbusVideo",
|
|
35
|
+
group: "block",
|
|
36
|
+
atom: true,
|
|
37
|
+
draggable: true,
|
|
38
|
+
addOptions() {
|
|
39
|
+
return { HTMLAttributes: {} };
|
|
40
|
+
},
|
|
41
|
+
addAttributes() {
|
|
42
|
+
return {
|
|
43
|
+
src: { default: null },
|
|
44
|
+
width: { default: null }
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
parseHTML() {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
tag: "div[data-mbus-src]",
|
|
51
|
+
getAttrs: (dom) => {
|
|
52
|
+
const el = dom;
|
|
53
|
+
return {
|
|
54
|
+
src: el.getAttribute("data-mbus-src"),
|
|
55
|
+
width: el.getAttribute("data-mbus-width") || el.style?.width || null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
renderHTML({ HTMLAttributes }) {
|
|
62
|
+
const attrs = {
|
|
63
|
+
"data-mbus-src": HTMLAttributes.src
|
|
64
|
+
};
|
|
65
|
+
if (HTMLAttributes.width) {
|
|
66
|
+
attrs["data-mbus-width"] = HTMLAttributes.width;
|
|
67
|
+
attrs["style"] = `width: ${HTMLAttributes.width}`;
|
|
68
|
+
}
|
|
69
|
+
return ["div", mergeAttributes(this.options.HTMLAttributes, attrs)];
|
|
70
|
+
},
|
|
71
|
+
addNodeView() {
|
|
72
|
+
return ({ node, editor, getPos }) => {
|
|
73
|
+
const dom = document.createElement("div");
|
|
74
|
+
dom.setAttribute("data-type", "mbusVideo");
|
|
75
|
+
dom.setAttribute("data-node-view-wrapper", "");
|
|
76
|
+
dom.style.cssText = "margin:8px 0;position:relative;box-sizing:border-box;max-width:100%;";
|
|
77
|
+
if (node.attrs.width)
|
|
78
|
+
dom.style.width = node.attrs.width;
|
|
79
|
+
const video = document.createElement("video");
|
|
80
|
+
video.controls = true;
|
|
81
|
+
video.preload = "metadata";
|
|
82
|
+
video.setAttribute("controlslist", "nodownload");
|
|
83
|
+
video.setAttribute("playsinline", "");
|
|
84
|
+
video.style.cssText =
|
|
85
|
+
"width:100%;height:auto;border-radius:8px;background:#0b1020;display:block;";
|
|
86
|
+
dom.appendChild(video);
|
|
87
|
+
const handle = node.attrs.src ? attachHls(video, node.attrs.src) : null;
|
|
88
|
+
if (editor.isEditable) {
|
|
89
|
+
const del = document.createElement("button");
|
|
90
|
+
del.type = "button";
|
|
91
|
+
del.textContent = "\u00D7";
|
|
92
|
+
del.style.cssText =
|
|
93
|
+
"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;";
|
|
94
|
+
del.addEventListener("click", () => {
|
|
95
|
+
const pos = typeof getPos === "function" ? getPos() : null;
|
|
96
|
+
if (pos != null) {
|
|
97
|
+
editor.commands.deleteRange({ from: pos, to: pos + node.nodeSize });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
dom.appendChild(del);
|
|
101
|
+
const resizeHandle = document.createElement("button");
|
|
102
|
+
resizeHandle.type = "button";
|
|
103
|
+
resizeHandle.contentEditable = "false";
|
|
104
|
+
resizeHandle.setAttribute("aria-label", "영상 너비 조절");
|
|
105
|
+
resizeHandle.style.cssText =
|
|
106
|
+
"position:absolute;right:-12px;top:0;bottom:0;width:14px;padding:0;margin:0;border:0;background:transparent;cursor:ew-resize;z-index:2;display:flex;align-items:center;justify-content:center;";
|
|
107
|
+
const bar = document.createElement("span");
|
|
108
|
+
bar.style.cssText =
|
|
109
|
+
"display:block;width:3px;height:48px;background:var(--border, #d1d5db);border-radius:2px;transition:background 0.15s;pointer-events:none;";
|
|
110
|
+
resizeHandle.appendChild(bar);
|
|
111
|
+
resizeHandle.addEventListener("mouseenter", () => {
|
|
112
|
+
bar.style.background = "var(--primary, #3382f2)";
|
|
113
|
+
});
|
|
114
|
+
resizeHandle.addEventListener("mouseleave", () => {
|
|
115
|
+
if (!resizing)
|
|
116
|
+
bar.style.background = "var(--border, #d1d5db)";
|
|
117
|
+
});
|
|
118
|
+
const MIN_W = 240;
|
|
119
|
+
const MAX_W = 1600;
|
|
120
|
+
let resizing = false;
|
|
121
|
+
let startX = 0;
|
|
122
|
+
let startWidth = 0;
|
|
123
|
+
resizeHandle.addEventListener("pointerdown", (e) => {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
e.stopPropagation();
|
|
126
|
+
resizing = true;
|
|
127
|
+
startX = e.clientX;
|
|
128
|
+
startWidth = dom.getBoundingClientRect().width;
|
|
129
|
+
bar.style.background = "var(--primary, #3382f2)";
|
|
130
|
+
resizeHandle.setPointerCapture(e.pointerId);
|
|
131
|
+
});
|
|
132
|
+
resizeHandle.addEventListener("pointermove", (e) => {
|
|
133
|
+
if (!resizing)
|
|
134
|
+
return;
|
|
135
|
+
const delta = e.clientX - startX;
|
|
136
|
+
const next = Math.max(MIN_W, Math.min(MAX_W, startWidth + delta));
|
|
137
|
+
dom.style.width = `${next}px`;
|
|
138
|
+
});
|
|
139
|
+
const endResize = (e) => {
|
|
140
|
+
if (!resizing)
|
|
141
|
+
return;
|
|
142
|
+
resizing = false;
|
|
143
|
+
bar.style.background = "var(--border, #d1d5db)";
|
|
144
|
+
try {
|
|
145
|
+
resizeHandle.releasePointerCapture(e.pointerId);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// ignore
|
|
149
|
+
}
|
|
150
|
+
const finalWidth = dom.style.width;
|
|
151
|
+
if (!finalWidth)
|
|
152
|
+
return;
|
|
153
|
+
const pos = typeof getPos === "function" ? getPos() : null;
|
|
154
|
+
if (pos == null)
|
|
155
|
+
return;
|
|
156
|
+
editor.view.dispatch(editor.view.state.tr.setNodeMarkup(pos, undefined, {
|
|
157
|
+
...node.attrs,
|
|
158
|
+
width: finalWidth
|
|
159
|
+
}));
|
|
160
|
+
};
|
|
161
|
+
resizeHandle.addEventListener("pointerup", endResize);
|
|
162
|
+
resizeHandle.addEventListener("pointercancel", endResize);
|
|
163
|
+
dom.appendChild(resizeHandle);
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
dom,
|
|
167
|
+
update: (updated) => {
|
|
168
|
+
if (updated.type !== node.type)
|
|
169
|
+
return false;
|
|
170
|
+
const w = updated.attrs.width;
|
|
171
|
+
dom.style.width = w || "";
|
|
172
|
+
return true;
|
|
173
|
+
},
|
|
174
|
+
destroy: () => handle?.destroy()
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
addCommands() {
|
|
179
|
+
return {
|
|
180
|
+
setMbusVideo: (attrs) => ({ chain }) => {
|
|
181
|
+
return chain().insertContent({ type: this.name, attrs }).run();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export { FixedDetails } from "./extensions/FixedDetails";
|
|
|
11
11
|
export { FileAttachment } from "./extensions/FileAttachment";
|
|
12
12
|
export type { FileResolver, FileResolveResult } from "./extensions/FileAttachment";
|
|
13
13
|
export { VideoBlock } from "./extensions/VideoBlock";
|
|
14
|
+
export { MbusVideo } from "./extensions/MbusVideo";
|
|
14
15
|
export { Columns } from "./extensions/Columns";
|
|
15
16
|
export { Column } from "./extensions/Column";
|
|
17
|
+
export { MathInline, MathDisplay } from "./extensions/Math";
|
|
16
18
|
export { sanitizeHtml, stripHtmlToExcerpt, transformLegacyHtml } from "./utils/sanitize";
|
|
17
19
|
export { configurePdfJs, getPdfJs } from "./utils/pdf";
|
|
18
20
|
export { cn } from "./utils/cn";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAGhC,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,8 +12,10 @@ export { Indent } from "./extensions/Indent";
|
|
|
12
12
|
export { FixedDetails } from "./extensions/FixedDetails";
|
|
13
13
|
export { FileAttachment } from "./extensions/FileAttachment";
|
|
14
14
|
export { VideoBlock } from "./extensions/VideoBlock";
|
|
15
|
+
export { MbusVideo } from "./extensions/MbusVideo";
|
|
15
16
|
export { Columns } from "./extensions/Columns";
|
|
16
17
|
export { Column } from "./extensions/Column";
|
|
18
|
+
export { MathInline, MathDisplay } from "./extensions/Math";
|
|
17
19
|
// 유틸리티
|
|
18
20
|
export { sanitizeHtml, stripHtmlToExcerpt, transformLegacyHtml } from "./utils/sanitize";
|
|
19
21
|
export { configurePdfJs, getPdfJs } from "./utils/pdf";
|
package/dist/styles/editor.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.top-full{top:100%}.z-30{z-index:30}.z-50{z-index:50}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-9{height:2.25rem}.h-px{height:1px}.w-4{width:1rem}.w-\[360px\]{width:360px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-\[90vw\]{max-width:90vw}.flex-shrink{flex-shrink:1}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.columns-2{-moz-columns:2;column-count:2}.columns-3{-moz-columns:3;column-count:3}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:var(--radius-md,4px)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-xl,8px)}.rounded-md{border-radius:var(--radius-md,6px)}.rounded-xl{border-radius:var(--radius-2xl,12px)}.rounded-t-xl{border-top-left-radius:var(--radius-2xl,12px);border-top-right-radius:var(--radius-2xl,12px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-border{border-color:var(--border)}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.bg-background{background-color:var(--background)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/40{background-color:rgba(0,0,0,.4)}.bg-border{background-color:var(--border)}.bg-foreground{background-color:var(--foreground)}.bg-muted{background-color:var(--muted)}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-muted-foreground{color:var(--muted-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/70{color:hsla(0,0%,100%,.7)}.text-white\/80{color:hsla(0,0%,100%,.8)}.underline{text-decoration-line:underline}.opacity-30{opacity:.3}.shadow-lg{--tw-shadow:var(--shadow-dropdown,0 10px 15px -3px rgba(0,0,0,.1));--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:var(--shadow-dropdown,0 1px 2px 0 rgba(0,0,0,.05));--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:var(--shadow-popup,0 20px 25px -5px rgba(0,0,0,.1));--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hce-editor-wrapper{overflow:visible!important}.bubble-toolbar-container button,.hce-editor-wrapper button,.slash-menu button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;cursor:pointer;box-sizing:border-box}.bubble-toolbar-container button:disabled,.hce-editor-wrapper button:disabled,.slash-menu button:disabled{cursor:default}.tiptap{font-size:1.0625rem;line-height:1.8;color:var(--foreground);padding:1.5rem}.tiptap>*+*{margin-top:.25em}.tiptap p{margin:.1em 0!important;line-height:1.5!important}.tiptap p:empty,.tiptap p:has(>br:only-child){min-height:1.2em}.tiptap h1,.tiptap h2,.tiptap h3,.tiptap li,.tiptap p{white-space:pre-wrap}.tiptap h1{font-size:2rem;font-weight:700;margin:1.5em 0 .5em;line-height:1.25}.tiptap h2{font-size:1.5rem;font-weight:600;margin:1.2em 0 .4em;line-height:1.3}.tiptap h3{font-size:1.25rem;font-weight:600;margin:1em 0 .3em;line-height:1.35}.tiptap ul{list-style:disc}.tiptap ol,.tiptap ul{padding-left:1.5em;margin:.5em 0}.tiptap ol{list-style:decimal}.tiptap ul ul{list-style:circle}.tiptap ul ul ul{list-style:square}.tiptap ol ol{list-style:lower-alpha}.tiptap ol ol ol{list-style:lower-roman}.tiptap li{margin:.25em 0}.tiptap ul[data-type=taskList]{list-style:none;padding-left:0}.tiptap ul[data-type=taskList] li{display:flex;align-items:baseline;gap:.5rem;margin:.1em 0;list-style:none;list-style-type:none}.tiptap ul[data-type=taskList] li>label{flex-shrink:0;display:flex;align-items:center;height:1.5em}.tiptap ul[data-type=taskList] li>label input[type=checkbox]{accent-color:var(--primary);width:1.1em;height:1.1em;cursor:pointer;margin:0}.tiptap ul[data-type=taskList] li>div{flex:1}.tiptap ul[data-type=taskList] li[data-checked=true]>div{text-decoration:line-through;opacity:.6}.tiptap blockquote{border-left:3px solid var(--border);padding:.5rem 1rem;margin:.75em 0;color:var(--muted-foreground);font-style:italic}.tiptap hr{border:none;border-top:2px solid color-mix(in srgb,var(--foreground) 38%,transparent);margin:1.5em 0}.tiptap code{background:var(--muted);border-radius:4px;padding:.15em .35em;font-size:.9em}.tiptap pre{background:#282c34;color:#abb2bf;border-radius:8px;padding:1em;margin:.75em 0;overflow-x:auto;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:.9em;line-height:1.6}.tiptap pre code{background:none;padding:0;border-radius:0;color:#abb2bf;font-size:inherit}.tiptap pre code .hljs-keyword,.tiptap pre code .hljs-operator{color:#c678dd}.tiptap pre code .hljs-addition,.tiptap pre code .hljs-doctag,.tiptap pre code .hljs-regexp,.tiptap pre code .hljs-string{color:#98c379}.tiptap pre code .hljs-attr,.tiptap pre code .hljs-number,.tiptap pre code .hljs-selector-class,.tiptap pre code .hljs-template-variable,.tiptap pre code .hljs-type,.tiptap pre code .hljs-variable{color:#d19a66}.tiptap pre code .hljs-function,.tiptap pre code .hljs-section,.tiptap pre code .hljs-title,.tiptap pre code .hljs-title.class_{color:#61aeee}.tiptap pre code .hljs-params,.tiptap pre code .hljs-punctuation{color:#abb2bf}.tiptap pre code .hljs-comment,.tiptap pre code .hljs-quote{color:#5c6370;font-style:italic}.tiptap pre code .hljs-built_in,.tiptap pre code .hljs-formula{color:#e6c07b}.tiptap pre code .hljs-meta,.tiptap pre code .hljs-meta .hljs-keyword{color:#61aeee}.tiptap pre code .hljs-meta .hljs-string{color:#98c379}.tiptap pre code .hljs-attribute,.tiptap pre code .hljs-deletion,.tiptap pre code .hljs-name,.tiptap pre code .hljs-property,.tiptap pre code .hljs-selector-tag,.tiptap pre code .hljs-subst,.tiptap pre code .hljs-tag{color:#e06c75}.tiptap pre code .hljs-literal{color:#56b6c2}.tiptap pre code .hljs-selector-attr,.tiptap pre code .hljs-selector-id,.tiptap pre code .hljs-selector-pseudo{color:#61aeee}.tiptap pre code .hljs-link{color:#61aeee;text-decoration:underline}.tiptap pre code .hljs-bullet,.tiptap pre code .hljs-symbol{color:#61aeee}.tiptap pre code .hljs-emphasis{font-style:italic}.tiptap pre code .hljs-strong{font-weight:700}.tiptap img{max-width:100%;height:auto;border-radius:8px;margin:.75em 0;cursor:pointer;transition:box-shadow .15s}.tiptap div[data-youtube-video].ProseMirror-selectednode iframe{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 25%,transparent)}.tiptap [data-type=pdfBlock].ProseMirror-selectednode>div{border-radius:8px}.tiptap a{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.tiptap a:hover{opacity:.8}.tiptap mark{background-color:#fef08a;border-radius:2px;padding:.05em .1em}.tiptap [data-type=details]{border:none;border-radius:0;padding:.25em 0;margin:.5em 0;background:transparent;display:flex;align-items:flex-start;gap:.25em}.tiptap [data-type=details]>button{background:none;border:none;cursor:pointer;padding:0;width:1.4em;height:1.4em;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-foreground);border-radius:4px;transition:transform .15s,background .15s;flex-shrink:0;margin-top:.25em}.tiptap [data-type=details]>button:after{content:"";display:block;width:0;height:0;border-left:5px solid;border-top:4px solid transparent;border-bottom:4px solid transparent;margin-left:2px}.tiptap [data-type=details]>button:hover{background:var(--muted);color:var(--foreground)}.tiptap [data-type=details].is-open>button{transform:rotate(90deg)}.tiptap [data-type=details]>div{flex:1;min-width:0}.tiptap [data-type=details] summary{font-weight:600;cursor:text;padding:.25em 0;display:block}.tiptap [data-type=detailsContent]{margin-top:.5em;padding-left:.5em;border-left:2px solid var(--border)}.tiptap [data-type=detailsContent][hidden]{display:none}.tiptap details{border:1px solid var(--border);border-radius:8px;padding:.5em 1em;margin:.75em 0;background:var(--background)}.tiptap details summary{font-weight:600;cursor:pointer;padding:.25em 0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.tiptap details summary::marker{color:var(--muted-foreground)}.tiptap details[open]>:not(summary){margin-top:.5em}.tiptap div[data-youtube-video]{margin:1em 0}.tiptap div[data-youtube-video] iframe{width:100%;aspect-ratio:16/9;border-radius:8px;border:none}.tiptap table{border-collapse:collapse;margin:1em 0;table-layout:fixed;border:2px solid var(--border);max-width:100%}.tiptap .tableWrapper{overflow-x:auto;max-width:100%}.tiptap td,.tiptap th{border:1px solid var(--border);padding:.6em .8em;min-width:60px;vertical-align:middle;position:relative;box-sizing:border-box;overflow:hidden;word-break:break-word;line-height:1.5}.tiptap th{background:var(--muted);font-weight:600;text-align:left}.tiptap td{background:var(--background)}.tiptap .selectedCell:after{content:"";position:absolute;inset:0;background:var(--primary);opacity:.12;pointer-events:none;z-index:1}.tiptap .column-resize-handle{position:absolute;right:-3px;top:0;bottom:0;width:6px;background:var(--primary);opacity:0;cursor:col-resize;z-index:20;border-radius:2px;transition:opacity .15s}.tiptap table:hover .column-resize-handle{opacity:.4}.tiptap .column-resize-handle:hover{opacity:1}.tiptap.resize-cursor{cursor:col-resize}.tableWrapper{overflow-x:auto;overflow-y:hidden;margin:1em 0;max-width:100%;padding-right:4px}.tiptap table .grip-column,.tiptap table .grip-row,.tiptap table .grip-table{display:none!important}.tiptap .is-empty:before{content:attr(data-placeholder);float:left;color:var(--muted-foreground);opacity:.5;pointer-events:none;height:0}.tiptap h1.is-empty:before{font-size:2rem;font-weight:700}.tiptap h2.is-empty:before{font-size:1.5rem;font-weight:600}.tiptap h3.is-empty:before{font-size:1.25rem;font-weight:600}.ProseMirror-gapcursor{display:none;pointer-events:none;position:relative}.ProseMirror-gapcursor:after{content:"";display:block;position:absolute;top:-2px;width:20px;border-top:1px solid var(--foreground);animation:ProseMirror-cursor-blink 1.1s steps(2,start) infinite}@keyframes ProseMirror-cursor-blink{to{visibility:hidden}}.ProseMirror-focused .ProseMirror-gapcursor{display:block}.ProseMirror-dropcursor{border-top:2px solid var(--primary);margin:-1px 0}.tiptap ::-moz-selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.tiptap ::selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.ProseMirror-noderangeselection ::-moz-selection{background:transparent}.ProseMirror-noderangeselection ::selection{background:transparent}.ProseMirror-noderangeselection *{caret-color:transparent}.ProseMirror-selectednode,.ProseMirror-selectednoderange{position:relative}.ProseMirror-selectednode:not(img):not([data-youtube-video]):not([data-type=pdfBlock]):before,.ProseMirror-selectednoderange:not([data-type=pdfBlock]):before{content:"";position:absolute;inset:-.25rem;background-color:color-mix(in srgb,var(--primary) 10%,transparent);border:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:.5rem;pointer-events:none;z-index:-1}.bubble-toolbar-container{z-index:50}.hce-floating-plus{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:var(--background);color:var(--muted-foreground);cursor:pointer;transition:all .15s}.hce-floating-plus:hover{color:var(--foreground);border-color:var(--foreground);background:var(--muted)}.hce-drag-handle{display:flex;align-items:center;gap:2px}.hce-drag-handle__plus{display:flex;align-items:center;justify-content:center;padding:4px;border-radius:4px;border:none;background:transparent;color:var(--muted-foreground);opacity:.5;cursor:pointer;transition:opacity .15s,background-color .15s}.hce-drag-handle__plus:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip{display:flex;align-items:center;justify-content:center;padding:2px;border-radius:4px;color:var(--muted-foreground);opacity:.5;cursor:grab;transition:opacity .15s,background-color .15s}.hce-drag-handle__grip:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip:active{cursor:grabbing}.tiptap div[data-type=columns]{display:grid;gap:24px;margin:1em 0}.tiptap div[data-type=columns][data-columns="2"]{grid-template-columns:1fr 1fr}.tiptap div[data-type=columns][data-columns="3"]{grid-template-columns:1fr 1fr 1fr}.tiptap div[data-type=column]{min-width:0;padding:4px 8px;border:1px dashed var(--border);border-radius:8px}.tiptap[contenteditable=true] div[data-type=column]:focus-within{border-color:var(--primary);border-style:solid}.tiptap div[data-type=columns].ProseMirror-selectednode{outline:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:8px}@media (max-width:640px){.tiptap div[data-type=columns]{grid-template-columns:1fr!important}}.hce-editor-wrapper button[data-tooltip]{position:relative}.hce-editor-wrapper button[data-tooltip]:hover:after{content:attr(data-tooltip);position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;z-index:60}.hce-table-btn-wrap{position:relative;display:inline-flex}.hce-table-btn-tooltip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;opacity:0;transition:opacity .15s;z-index:60}.hce-table-btn-wrap:hover .hce-table-btn-tooltip{opacity:1}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.focus\:border-primary:focus{border-color:var(--primary)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.disabled\:opacity-30:disabled{opacity:.3}
|
|
1
|
+
.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.top-full{top:100%}.z-30{z-index:30}.z-50{z-index:50}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-px{height:1px}.w-10{width:2.5rem}.w-4{width:1rem}.w-\[360px\]{width:360px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-\[90vw\]{max-width:90vw}.flex-shrink{flex-shrink:1}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.columns-2{-moz-columns:2;column-count:2}.columns-3{-moz-columns:3;column-count:3}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:var(--radius-md,4px)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-xl,8px)}.rounded-md{border-radius:var(--radius-md,6px)}.rounded-xl{border-radius:var(--radius-2xl,12px)}.rounded-t-xl{border-top-left-radius:var(--radius-2xl,12px);border-top-right-radius:var(--radius-2xl,12px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-border{border-color:var(--border)}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.bg-background{background-color:var(--background)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/40{background-color:rgba(0,0,0,.4)}.bg-border{background-color:var(--border)}.bg-foreground{background-color:var(--foreground)}.bg-muted{background-color:var(--muted)}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-muted-foreground{color:var(--muted-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/70{color:hsla(0,0%,100%,.7)}.text-white\/80{color:hsla(0,0%,100%,.8)}.underline{text-decoration-line:underline}.opacity-30{opacity:.3}.shadow-lg{--tw-shadow:var(--shadow-dropdown,0 10px 15px -3px rgba(0,0,0,.1));--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:var(--shadow-dropdown,0 1px 2px 0 rgba(0,0,0,.05));--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:var(--shadow-popup,0 20px 25px -5px rgba(0,0,0,.1));--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hce-editor-wrapper{overflow:visible!important}.bubble-toolbar-container button,.hce-editor-wrapper button,.slash-menu button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;cursor:pointer;box-sizing:border-box}.bubble-toolbar-container button:disabled,.hce-editor-wrapper button:disabled,.slash-menu button:disabled{cursor:default}.tiptap{font-size:1.0625rem;line-height:1.8;color:var(--foreground);padding:1.5rem}.tiptap>*+*{margin-top:.25em}.tiptap p{margin:.1em 0!important;line-height:1.5!important}.tiptap p:empty,.tiptap p:has(>br:only-child){min-height:1.2em}.tiptap h1,.tiptap h2,.tiptap h3,.tiptap li,.tiptap p{white-space:pre-wrap}.tiptap h1{font-size:2rem;font-weight:700;margin:1.5em 0 .5em;line-height:1.25}.tiptap h2{font-size:1.5rem;font-weight:600;margin:1.2em 0 .4em;line-height:1.3}.tiptap h3{font-size:1.25rem;font-weight:600;margin:1em 0 .3em;line-height:1.35}.tiptap ul{list-style:disc}.tiptap ol,.tiptap ul{padding-left:1.5em;margin:.5em 0}.tiptap ol{list-style:decimal}.tiptap ul ul{list-style:circle}.tiptap ul ul ul{list-style:square}.tiptap ol ol{list-style:lower-alpha}.tiptap ol ol ol{list-style:lower-roman}.tiptap li{margin:.25em 0}.tiptap ul[data-type=taskList]{list-style:none;padding-left:0}.tiptap ul[data-type=taskList] li{display:flex;align-items:baseline;gap:.5rem;margin:.1em 0;list-style:none;list-style-type:none}.tiptap ul[data-type=taskList] li>label{flex-shrink:0;display:flex;align-items:center;height:1.5em}.tiptap ul[data-type=taskList] li>label input[type=checkbox]{accent-color:var(--primary);width:1.1em;height:1.1em;cursor:pointer;margin:0}.tiptap ul[data-type=taskList] li>div{flex:1}.tiptap ul[data-type=taskList] li[data-checked=true]>div{text-decoration:line-through;opacity:.6}.tiptap blockquote{border-left:3px solid var(--border);padding:.5rem 1rem;margin:.75em 0;color:var(--muted-foreground);font-style:italic}.tiptap hr{border:none;border-top:2px solid color-mix(in srgb,var(--foreground) 38%,transparent);margin:1.5em 0}.tiptap code{background:var(--muted);border-radius:4px;padding:.15em .35em;font-size:.9em}.tiptap pre{background:#282c34;color:#abb2bf;border-radius:8px;padding:1em;margin:.75em 0;overflow-x:auto;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:.9em;line-height:1.6}.tiptap pre code{background:none;padding:0;border-radius:0;color:#abb2bf;font-size:inherit}.tiptap pre code .hljs-keyword,.tiptap pre code .hljs-operator{color:#c678dd}.tiptap pre code .hljs-addition,.tiptap pre code .hljs-doctag,.tiptap pre code .hljs-regexp,.tiptap pre code .hljs-string{color:#98c379}.tiptap pre code .hljs-attr,.tiptap pre code .hljs-number,.tiptap pre code .hljs-selector-class,.tiptap pre code .hljs-template-variable,.tiptap pre code .hljs-type,.tiptap pre code .hljs-variable{color:#d19a66}.tiptap pre code .hljs-function,.tiptap pre code .hljs-section,.tiptap pre code .hljs-title,.tiptap pre code .hljs-title.class_{color:#61aeee}.tiptap pre code .hljs-params,.tiptap pre code .hljs-punctuation{color:#abb2bf}.tiptap pre code .hljs-comment,.tiptap pre code .hljs-quote{color:#5c6370;font-style:italic}.tiptap pre code .hljs-built_in,.tiptap pre code .hljs-formula{color:#e6c07b}.tiptap pre code .hljs-meta,.tiptap pre code .hljs-meta .hljs-keyword{color:#61aeee}.tiptap pre code .hljs-meta .hljs-string{color:#98c379}.tiptap pre code .hljs-attribute,.tiptap pre code .hljs-deletion,.tiptap pre code .hljs-name,.tiptap pre code .hljs-property,.tiptap pre code .hljs-selector-tag,.tiptap pre code .hljs-subst,.tiptap pre code .hljs-tag{color:#e06c75}.tiptap pre code .hljs-literal{color:#56b6c2}.tiptap pre code .hljs-selector-attr,.tiptap pre code .hljs-selector-id,.tiptap pre code .hljs-selector-pseudo{color:#61aeee}.tiptap pre code .hljs-link{color:#61aeee;text-decoration:underline}.tiptap pre code .hljs-bullet,.tiptap pre code .hljs-symbol{color:#61aeee}.tiptap pre code .hljs-emphasis{font-style:italic}.tiptap pre code .hljs-strong{font-weight:700}.tiptap img{max-width:100%;height:auto;border-radius:8px;margin:.75em 0;cursor:pointer;transition:box-shadow .15s}.tiptap div[data-youtube-video].ProseMirror-selectednode iframe{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 25%,transparent)}.tiptap [data-type=pdfBlock].ProseMirror-selectednode>div{border-radius:8px}.tiptap a{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.tiptap a:hover{opacity:.8}.tiptap mark{background-color:#fef08a;border-radius:2px;padding:.05em .1em}.tiptap [data-type=details]{border:none;border-radius:0;padding:0;margin:.5em 0;background:transparent;display:grid;grid-template-columns:26px 1fr;align-items:start}.tiptap [data-type=details]>button{background:none;border:none;cursor:pointer;padding:0;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-foreground);border-radius:4px;transition:transform .2s,background .15s;flex-shrink:0;margin-top:11px}.tiptap [data-type=details]>button:after{content:"";display:block;width:0;height:0;border-left:5px solid;border-top:4px solid transparent;border-bottom:4px solid transparent;margin-left:2px}.tiptap [data-type=details]>button:hover{background:var(--muted);color:var(--foreground)}.tiptap [data-type=details].is-open>button{transform:rotate(90deg)}.tiptap [data-type=details]>div{flex:1;min-width:0}.tiptap [data-type=details] summary{font-weight:600;cursor:text;padding:10px 0;display:block;border-bottom:1px solid var(--border,#f6f7f9)}.tiptap [data-type=detailsContent]{padding:8px 0;border-left:none}.tiptap [data-type=detailsContent][hidden]{display:none}.tiptap details{border:none;border-radius:0;padding:0;margin:.5em 0;background:transparent}.tiptap details summary{font-weight:600;cursor:pointer;padding:10px 0 10px 26px;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-bottom:1px solid var(--border,#f6f7f9);position:relative;list-style:none}.tiptap details summary::-webkit-details-marker{display:none}.tiptap details summary::marker{content:""}.tiptap details summary:before{content:"";position:absolute;left:4px;top:50%;width:0;height:0;border-left:5px solid var(--muted-foreground);border-top:4px solid transparent;border-bottom:4px solid transparent;transform:translateY(-50%);transition:transform .2s}.tiptap details[open]>summary:before{transform:translateY(-50%) rotate(90deg)}.tiptap details[open]>:not(summary){margin-top:8px;padding-left:26px}.tiptap div[data-youtube-video]{margin:1em 0}.tiptap div[data-youtube-video] iframe{width:100%;aspect-ratio:16/9;border-radius:8px;border:none}.tiptap table{border-collapse:collapse;margin:1em 0;table-layout:fixed;border:2px solid var(--border);max-width:100%}.tiptap .tableWrapper{overflow-x:auto;max-width:100%}.tiptap td,.tiptap th{border:1px solid var(--border);padding:.6em .8em;min-width:60px;vertical-align:middle;position:relative;box-sizing:border-box;overflow:hidden;word-break:break-word;line-height:1.5}.tiptap th{background:var(--muted);font-weight:600;text-align:left}.tiptap td{background:var(--background)}.tiptap .selectedCell:after{content:"";position:absolute;inset:0;background:var(--primary);opacity:.12;pointer-events:none;z-index:1}.tiptap .column-resize-handle{position:absolute;right:-3px;top:0;bottom:0;width:6px;background:var(--primary);opacity:0;cursor:col-resize;z-index:20;border-radius:2px;transition:opacity .15s}.tiptap table:hover .column-resize-handle{opacity:.4}.tiptap .column-resize-handle:hover{opacity:1}.tiptap.resize-cursor{cursor:col-resize}.tableWrapper{overflow-x:auto;overflow-y:hidden;margin:1em 0;max-width:100%;padding-right:4px}.tiptap table .grip-column,.tiptap table .grip-row,.tiptap table .grip-table{display:none!important}.tiptap .is-empty:before{content:attr(data-placeholder);float:left;color:var(--muted-foreground);opacity:.5;pointer-events:none;height:0}.tiptap h1.is-empty:before{font-size:2rem;font-weight:700}.tiptap h2.is-empty:before{font-size:1.5rem;font-weight:600}.tiptap h3.is-empty:before{font-size:1.25rem;font-weight:600}.ProseMirror-gapcursor{display:none;pointer-events:none;position:relative}.ProseMirror-gapcursor:after{content:"";display:block;position:absolute;top:-2px;width:20px;border-top:1px solid var(--foreground);animation:ProseMirror-cursor-blink 1.1s steps(2,start) infinite}@keyframes ProseMirror-cursor-blink{to{visibility:hidden}}.ProseMirror-focused .ProseMirror-gapcursor{display:block}.ProseMirror-dropcursor{border-top:2px solid var(--primary);margin:-1px 0}.tiptap ::-moz-selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.tiptap ::selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.ProseMirror-noderangeselection ::-moz-selection{background:transparent}.ProseMirror-noderangeselection ::selection{background:transparent}.ProseMirror-noderangeselection *{caret-color:transparent}.ProseMirror-selectednode,.ProseMirror-selectednoderange{position:relative}.ProseMirror-selectednode:not(img):not([data-youtube-video]):not([data-type=pdfBlock]):before,.ProseMirror-selectednoderange:not([data-type=pdfBlock]):before{content:"";position:absolute;inset:-.25rem;background-color:color-mix(in srgb,var(--primary) 10%,transparent);border:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:.5rem;pointer-events:none;z-index:-1}.bubble-toolbar-container{z-index:50}.hce-floating-plus{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:var(--background);color:var(--muted-foreground);cursor:pointer;transition:all .15s}.hce-floating-plus:hover{color:var(--foreground);border-color:var(--foreground);background:var(--muted)}.hce-drag-handle{display:flex;align-items:center;gap:2px}.hce-drag-handle__plus{display:flex;align-items:center;justify-content:center;padding:4px;border-radius:4px;border:none;background:transparent;color:var(--muted-foreground);opacity:.5;cursor:pointer;transition:opacity .15s,background-color .15s}.hce-drag-handle__plus:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip{display:flex;align-items:center;justify-content:center;padding:2px;border-radius:4px;color:var(--muted-foreground);opacity:.5;cursor:grab;transition:opacity .15s,background-color .15s}.hce-drag-handle__grip:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip:active{cursor:grabbing}.tiptap div[data-type=columns]{display:grid;gap:24px;margin:1em 0}.tiptap div[data-type=columns][data-columns="2"]{grid-template-columns:1fr 1fr}.tiptap div[data-type=columns][data-columns="3"]{grid-template-columns:1fr 1fr 1fr}.tiptap div[data-type=column]{min-width:0;padding:4px 8px;border:1px dashed var(--border);border-radius:8px}.tiptap[contenteditable=true] div[data-type=column]:focus-within{border-color:var(--primary);border-style:solid}.tiptap div[data-type=columns].ProseMirror-selectednode{outline:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:8px}@media (max-width:640px){.tiptap div[data-type=columns]{grid-template-columns:1fr!important}}.hce-editor-wrapper button[data-tooltip]{position:relative}.hce-editor-wrapper button[data-tooltip]:hover:after{content:attr(data-tooltip);position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;z-index:60}.hce-table-btn-wrap{position:relative;display:inline-flex}.hce-table-btn-tooltip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;opacity:0;transition:opacity .15s;z-index:60}.hce-table-btn-wrap:hover .hce-table-btn-tooltip{opacity:1}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.focus\:border-primary:focus{border-color:var(--primary)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.disabled\:opacity-30:disabled{opacity:.3}
|
package/dist/types.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type PromptHandler = (defaultValue: string) => Promise<string | null>;
|
|
|
12
12
|
/** 툴바 모드 */
|
|
13
13
|
export type ToolbarMode = 'minimal' | 'standard' | 'full';
|
|
14
14
|
/** 개별 툴바 기능 ID */
|
|
15
|
-
export type ToolbarFeature = 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'superscript' | 'subscript' | 'code' | 'align-left' | 'align-center' | 'align-right' | 'h1' | 'h2' | 'h3' | 'bullet-list' | 'ordered-list' | 'checklist' | 'blockquote' | 'horizontal-rule' | 'toggle' | 'link' | 'image' | 'pdf' | 'youtube' | 'video' | 'file' | 'columns-2' | 'columns-3' | 'table' | 'code-block' | 'undo' | 'redo' | 'fixed-toolbar' | 'bubble-toolbar' | 'slash-menu' | 'table-menu' | 'character-count' | 'upload-overlay';
|
|
15
|
+
export type ToolbarFeature = 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'superscript' | 'subscript' | 'code' | 'text-color' | 'align-left' | 'align-center' | 'align-right' | 'h1' | 'h2' | 'h3' | 'bullet-list' | 'ordered-list' | 'checklist' | 'blockquote' | 'horizontal-rule' | 'toggle' | 'link' | 'image' | 'pdf' | 'youtube' | 'video' | 'file' | 'mbus' | 'columns-2' | 'columns-3' | 'table' | 'code-block' | 'undo' | 'redo' | 'fixed-toolbar' | 'bubble-toolbar' | 'slash-menu' | 'table-menu' | 'character-count' | 'upload-overlay';
|
|
16
16
|
/** 모드별 기본 feature 프리셋 */
|
|
17
17
|
export declare const TOOLBAR_PRESETS: Record<ToolbarMode, ToolbarFeature[]>;
|
|
18
18
|
/** features 배열 → Set 변환. features가 있으면 그걸 쓰고 없으면 toolbar 모드 프리셋 */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,+BAA+B;AAC/B,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7E,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,kBAAkB;AAClB,MAAM,MAAM,cAAc,GAEvB,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GACxD,aAAa,GAAG,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,+BAA+B;AAC/B,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7E,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,kBAAkB;AAClB,MAAM,MAAM,cAAc,GAEvB,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GACxD,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAEnD,YAAY,GAAG,cAAc,GAAG,aAAa,GAE7C,IAAI,GAAG,IAAI,GAAG,IAAI,GAElB,aAAa,GAAG,cAAc,GAAG,WAAW,GAC5C,YAAY,GAAG,iBAAiB,GAAG,QAAQ,GAE3C,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAEhE,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,GAElD,MAAM,GAAG,MAAM,GAEf,eAAe,GAAG,gBAAgB,GAAG,YAAY,GACjD,YAAY,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAEvD,yBAAyB;AACzB,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,EAAE,CA+BjE,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,eAAe,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,cAAc,EAAE,GACzB,GAAG,CAAC,cAAc,CAAC,CAErB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,uCAAuC;IACvC,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,yFAAyF;IACzF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,2CAA2C;IAC3C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC"}
|
package/dist/types.js
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
export const TOOLBAR_PRESETS = {
|
|
3
3
|
full: [
|
|
4
4
|
'bold', 'italic', 'underline', 'strike', 'highlight',
|
|
5
|
-
'superscript', 'subscript', 'code',
|
|
5
|
+
'superscript', 'subscript', 'code', 'text-color',
|
|
6
6
|
'align-left', 'align-center', 'align-right',
|
|
7
7
|
'h1', 'h2', 'h3',
|
|
8
8
|
'bullet-list', 'ordered-list', 'checklist',
|
|
9
9
|
'blockquote', 'horizontal-rule', 'toggle',
|
|
10
|
-
'link', 'image', 'pdf', 'youtube', 'video', 'file',
|
|
10
|
+
'link', 'image', 'pdf', 'youtube', 'video', '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',
|
|
17
|
+
'bold', 'italic', 'strike', 'code', 'text-color',
|
|
18
18
|
'h1', 'h2', 'h3',
|
|
19
19
|
'bullet-list', 'ordered-list', 'checklist',
|
|
20
20
|
'blockquote', 'horizontal-rule', 'toggle',
|
|
21
|
-
'link', 'image', 'pdf', 'youtube', 'video', 'file',
|
|
21
|
+
'link', 'image', 'pdf', 'youtube', 'video', 'file', 'mbus',
|
|
22
22
|
'columns-2', 'columns-3', 'table', 'code-block',
|
|
23
23
|
'fixed-toolbar', 'slash-menu', 'table-menu'
|
|
24
24
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/utils/sanitize.js
CHANGED
|
@@ -6,6 +6,7 @@ const ALLOWED_TAGS = new Set([
|
|
|
6
6
|
"table", "colgroup", "col", "thead", "tbody", "tr", "th", "td",
|
|
7
7
|
"span", "div", "hr", "sub", "sup",
|
|
8
8
|
"tiptap-midibus",
|
|
9
|
+
"math-inline", "math-display",
|
|
9
10
|
]);
|
|
10
11
|
const ALLOWED_ATTRS = {
|
|
11
12
|
a: new Set(["href", "title", "target", "rel"]),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teriusu/rich-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "TipTap 기반 Notion-style 리치 텍스트 에디터 (Svelte)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/index.js",
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
"@tiptap/starter-kit": "^3.22.0",
|
|
61
61
|
"clsx": "^2.1.1",
|
|
62
62
|
"highlight.js": "^11.11.1",
|
|
63
|
+
"hls.js": "^1.6.16",
|
|
64
|
+
"katex": "^0.16.45",
|
|
63
65
|
"lowlight": "^3.3.0",
|
|
64
66
|
"lucide-svelte": "^0.477.0",
|
|
65
67
|
"tailwind-merge": "^3.0.0"
|