@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.
@@ -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;AAqB3C,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;AAgPJ,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,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 modalState: { type: "link" | "image" | "cellBg" } | null = $state(null);
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({ language: 'cpp' }).run()}
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;AA4C3C,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;CAC/B,CAAC;AAonBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,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"}
@@ -122,7 +122,7 @@
122
122
  keywords: "code 코드 블록 cpp c++ python 파이썬",
123
123
  icon: Code2,
124
124
  command: (editor) =>
125
- editor.chain().focus().setCodeBlock({ language: "cpp" }).run(),
125
+ editor.chain().focus().setCodeBlock().run(),
126
126
  },
127
127
  {
128
128
  feature: "toggle",
@@ -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;AAa5D,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,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,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;AA+mBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,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,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,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,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,kCAqNzB,CAAC"}
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
- // ID만 있으면 resolver로 해결 시도
172
- nameEl.disabled = true;
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 = "resolve failed";
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: null,
224
- downloadBaseUrl: "/api/upload",
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";
@@ -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;AAG7C,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"}
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";
@@ -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 모드 프리셋 */
@@ -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,GAEpC,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,GAEvD,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"}
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":"AA2BA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcjD;AA0BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA4DxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAWrE"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AA4BA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcjD;AA0BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA4DxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAWrE"}
@@ -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.6.4",
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"