@teriusu/rich-editor 0.5.3 → 0.6.1
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/README.md +43 -10
- package/dist/components/BubbleToolbar.svelte +26 -16
- package/dist/components/BubbleToolbar.svelte.d.ts +2 -1
- package/dist/components/BubbleToolbar.svelte.d.ts.map +1 -1
- package/dist/components/FixedToolbar.svelte +100 -77
- package/dist/components/FixedToolbar.svelte.d.ts +3 -1
- package/dist/components/FixedToolbar.svelte.d.ts.map +1 -1
- package/dist/components/InputModal.svelte +19 -16
- package/dist/components/InputModal.svelte.d.ts.map +1 -1
- package/dist/components/SlashCommandMenu.svelte +186 -57
- package/dist/components/SlashCommandMenu.svelte.d.ts +3 -1
- package/dist/components/SlashCommandMenu.svelte.d.ts.map +1 -1
- package/dist/components/TableBubbleMenu.svelte +16 -16
- package/dist/components/TipTapEditor.svelte +13 -3
- package/dist/components/TipTapEditor.svelte.d.ts +3 -1
- package/dist/components/TipTapEditor.svelte.d.ts.map +1 -1
- package/dist/extensions/FileAttachment.d.ts.map +1 -1
- package/dist/extensions/FileAttachment.js +20 -16
- package/dist/extensions/PdfBlock.d.ts.map +1 -1
- package/dist/extensions/PdfBlock.js +84 -1
- package/dist/styles/editor.css +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -3
- package/package.json +2 -2
|
@@ -21,11 +21,35 @@
|
|
|
21
21
|
Columns2,
|
|
22
22
|
Columns3,
|
|
23
23
|
} from "lucide-svelte";
|
|
24
|
-
import type { SlashMenuItem, ToolbarFeature } from "../types";
|
|
24
|
+
import type { SlashMenuItem, ToolbarFeature, PromptHandler } from "../types";
|
|
25
25
|
import type { Component } from "svelte";
|
|
26
26
|
|
|
27
27
|
const SI = 14;
|
|
28
28
|
|
|
29
|
+
/** 각 feature가 속하는 섹션 라벨 */
|
|
30
|
+
const SECTION_MAP: Record<string, string> = {
|
|
31
|
+
"code-block": "자주 쓰는",
|
|
32
|
+
file: "자주 쓰는",
|
|
33
|
+
pdf: "자주 쓰는",
|
|
34
|
+
h1: "기본",
|
|
35
|
+
h2: "기본",
|
|
36
|
+
h3: "기본",
|
|
37
|
+
"bullet-list": "리스트",
|
|
38
|
+
"ordered-list": "리스트",
|
|
39
|
+
checklist: "리스트",
|
|
40
|
+
toggle: "리스트",
|
|
41
|
+
blockquote: "블록",
|
|
42
|
+
"horizontal-rule": "블록",
|
|
43
|
+
table: "레이아웃",
|
|
44
|
+
"columns-2": "레이아웃",
|
|
45
|
+
"columns-3": "레이아웃",
|
|
46
|
+
link: "미디어",
|
|
47
|
+
image: "미디어",
|
|
48
|
+
youtube: "미디어",
|
|
49
|
+
video: "미디어",
|
|
50
|
+
};
|
|
51
|
+
const SECTION_ORDER = ["자주 쓰는", "기본", "리스트", "블록", "레이아웃", "미디어"];
|
|
52
|
+
|
|
29
53
|
const SLASH_MENU_ITEMS_DATA: {
|
|
30
54
|
feature: ToolbarFeature;
|
|
31
55
|
label: string;
|
|
@@ -94,20 +118,12 @@
|
|
|
94
118
|
},
|
|
95
119
|
{
|
|
96
120
|
feature: "code-block",
|
|
97
|
-
label: "
|
|
98
|
-
keywords: "code cpp c++
|
|
121
|
+
label: "코드",
|
|
122
|
+
keywords: "code 코드 블록 cpp c++ python 파이썬",
|
|
99
123
|
icon: Code2,
|
|
100
124
|
command: (editor) =>
|
|
101
125
|
editor.chain().focus().setCodeBlock({ language: "cpp" }).run(),
|
|
102
126
|
},
|
|
103
|
-
{
|
|
104
|
-
feature: "code-block",
|
|
105
|
-
label: "Python 코드",
|
|
106
|
-
keywords: "code python 파이썬 코드 블록",
|
|
107
|
-
icon: Code2,
|
|
108
|
-
command: (editor) =>
|
|
109
|
-
editor.chain().focus().setCodeBlock({ language: "python" }).run(),
|
|
110
|
-
},
|
|
111
127
|
{
|
|
112
128
|
feature: "toggle",
|
|
113
129
|
label: "토글",
|
|
@@ -187,6 +203,8 @@
|
|
|
187
203
|
onPdfUpload,
|
|
188
204
|
onFileUpload,
|
|
189
205
|
onVideoUpload,
|
|
206
|
+
onPromptLink,
|
|
207
|
+
onPromptImage,
|
|
190
208
|
}: {
|
|
191
209
|
editor: Editor;
|
|
192
210
|
features: Set<ToolbarFeature>;
|
|
@@ -195,14 +213,45 @@
|
|
|
195
213
|
onPdfUpload?: () => void;
|
|
196
214
|
onFileUpload?: () => void;
|
|
197
215
|
onVideoUpload?: () => void;
|
|
216
|
+
onPromptLink?: PromptHandler;
|
|
217
|
+
onPromptImage?: PromptHandler;
|
|
198
218
|
} = $props();
|
|
199
219
|
|
|
220
|
+
async function runItem(item: (typeof SLASH_MENU_ITEMS_DATA)[number]) {
|
|
221
|
+
if (item.feature === "link" && onPromptLink) {
|
|
222
|
+
const url = await onPromptLink("");
|
|
223
|
+
if (url)
|
|
224
|
+
editor
|
|
225
|
+
.chain()
|
|
226
|
+
.focus()
|
|
227
|
+
.extendMarkRange("link")
|
|
228
|
+
.setLink({ href: url })
|
|
229
|
+
.run();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (item.feature === "image" && onPromptImage) {
|
|
233
|
+
const url = await onPromptImage("");
|
|
234
|
+
if (url) editor.chain().focus().setImage({ src: url }).run();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
item.command(editor);
|
|
238
|
+
}
|
|
239
|
+
|
|
200
240
|
let selectedIndex = $state(0);
|
|
201
241
|
let menuEl: HTMLDivElement | undefined = $state();
|
|
202
242
|
let mouseMovedSinceKeyboard = $state(true);
|
|
203
243
|
|
|
204
244
|
const allItems = $derived.by(() => {
|
|
205
245
|
const items = SLASH_MENU_ITEMS_DATA.filter((item) => features.has(item.feature));
|
|
246
|
+
if (onFileUpload) {
|
|
247
|
+
items.push({
|
|
248
|
+
feature: "file" as ToolbarFeature,
|
|
249
|
+
label: "파일 첨부",
|
|
250
|
+
keywords: "file attach 파일 첨부",
|
|
251
|
+
icon: Paperclip,
|
|
252
|
+
command: () => onFileUpload!(),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
206
255
|
if (onPdfUpload) {
|
|
207
256
|
items.push({
|
|
208
257
|
feature: "pdf" as ToolbarFeature,
|
|
@@ -221,15 +270,6 @@
|
|
|
221
270
|
command: () => onVideoUpload!(),
|
|
222
271
|
});
|
|
223
272
|
}
|
|
224
|
-
if (onFileUpload) {
|
|
225
|
-
items.push({
|
|
226
|
-
feature: "file" as ToolbarFeature,
|
|
227
|
-
label: "파일 첨부",
|
|
228
|
-
keywords: "file attach 파일 첨부",
|
|
229
|
-
icon: Paperclip,
|
|
230
|
-
command: () => onFileUpload!(),
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
273
|
return items;
|
|
234
274
|
});
|
|
235
275
|
|
|
@@ -258,6 +298,16 @@
|
|
|
258
298
|
}
|
|
259
299
|
});
|
|
260
300
|
|
|
301
|
+
$effect(() => {
|
|
302
|
+
function handleMouseDown(e: MouseEvent) {
|
|
303
|
+
if (menuEl && !menuEl.contains(e.target as Node)) {
|
|
304
|
+
onClose();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
308
|
+
return () => document.removeEventListener("mousedown", handleMouseDown, true);
|
|
309
|
+
});
|
|
310
|
+
|
|
261
311
|
$effect(() => {
|
|
262
312
|
function handleKeyDown(e: KeyboardEvent) {
|
|
263
313
|
if (e.key === "ArrowDown") {
|
|
@@ -274,8 +324,9 @@
|
|
|
274
324
|
} else if (e.key === "Enter") {
|
|
275
325
|
e.preventDefault();
|
|
276
326
|
if (filtered[selectedIndex]) {
|
|
327
|
+
const item = filtered[selectedIndex];
|
|
277
328
|
onClose();
|
|
278
|
-
|
|
329
|
+
runItem(item);
|
|
279
330
|
}
|
|
280
331
|
} else if (e.key === "Escape") {
|
|
281
332
|
e.preventDefault();
|
|
@@ -290,49 +341,127 @@
|
|
|
290
341
|
{#if filtered.length === 0}
|
|
291
342
|
<div
|
|
292
343
|
bind:this={menuEl}
|
|
293
|
-
class="z-50 bg-popover border border-border rounded-
|
|
294
|
-
style="width: 180px"
|
|
344
|
+
class="slash-menu z-50 bg-popover border border-border rounded-xl shadow-xl p-2"
|
|
295
345
|
>
|
|
296
346
|
<p class="text-xs text-muted-foreground px-2 py-1">결과 없음</p>
|
|
297
347
|
</div>
|
|
298
348
|
{:else}
|
|
299
349
|
<div
|
|
300
350
|
bind:this={menuEl}
|
|
301
|
-
class="z-50 bg-popover border border-border rounded-
|
|
302
|
-
style="width: 180px; max-height: 200px"
|
|
351
|
+
class="slash-menu z-50 bg-popover border border-border rounded-xl shadow-xl overflow-y-auto py-1.5"
|
|
303
352
|
>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
</button>
|
|
353
|
+
{#each SECTION_ORDER as section}
|
|
354
|
+
{@const sectionItems = filtered.filter(
|
|
355
|
+
(it) => (SECTION_MAP[it.feature] ?? '기본') === section,
|
|
356
|
+
)}
|
|
357
|
+
{#if sectionItems.length > 0}
|
|
358
|
+
<p class="slash-section">{section}</p>
|
|
359
|
+
{#each sectionItems as item}
|
|
360
|
+
{@const i = filtered.indexOf(item)}
|
|
361
|
+
<button
|
|
362
|
+
type="button"
|
|
363
|
+
data-index={i}
|
|
364
|
+
class="slash-item {i === selectedIndex ? 'is-selected' : ''}"
|
|
365
|
+
onmousemove={() => {
|
|
366
|
+
if (!mouseMovedSinceKeyboard) {
|
|
367
|
+
mouseMovedSinceKeyboard = true;
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
selectedIndex = i;
|
|
371
|
+
}}
|
|
372
|
+
onclick={() => {
|
|
373
|
+
onClose();
|
|
374
|
+
runItem(item);
|
|
375
|
+
}}
|
|
376
|
+
>
|
|
377
|
+
<span class="slash-icon">
|
|
378
|
+
<item.icon size={SI} />
|
|
379
|
+
</span>
|
|
380
|
+
<span class="slash-label">{item.label}</span>
|
|
381
|
+
</button>
|
|
382
|
+
{/each}
|
|
383
|
+
{/if}
|
|
336
384
|
{/each}
|
|
337
385
|
</div>
|
|
338
386
|
{/if}
|
|
387
|
+
|
|
388
|
+
<style>
|
|
389
|
+
.slash-menu {
|
|
390
|
+
width: 220px;
|
|
391
|
+
max-height: 320px;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.slash-section {
|
|
395
|
+
margin: 0;
|
|
396
|
+
padding: 6px 12px 4px;
|
|
397
|
+
font-size: var(--text-xs, 11px);
|
|
398
|
+
font-weight: 600;
|
|
399
|
+
color: var(--muted-foreground);
|
|
400
|
+
letter-spacing: 0.02em;
|
|
401
|
+
}
|
|
402
|
+
.slash-section:not(:first-of-type) {
|
|
403
|
+
margin-top: 4px;
|
|
404
|
+
padding-top: 8px;
|
|
405
|
+
border-top: 1px solid var(--border);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.slash-item {
|
|
409
|
+
position: relative;
|
|
410
|
+
display: flex;
|
|
411
|
+
align-items: center;
|
|
412
|
+
gap: 10px;
|
|
413
|
+
width: 100%;
|
|
414
|
+
padding: 6px 12px;
|
|
415
|
+
text-align: left;
|
|
416
|
+
font-size: var(--text-sm, 13px);
|
|
417
|
+
font-weight: 500;
|
|
418
|
+
color: var(--foreground);
|
|
419
|
+
background: transparent;
|
|
420
|
+
border: 0;
|
|
421
|
+
cursor: pointer;
|
|
422
|
+
transition: background-color 0.12s;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.slash-item:hover,
|
|
426
|
+
.slash-item.is-selected {
|
|
427
|
+
background: color-mix(in srgb, var(--primary) 8%, transparent);
|
|
428
|
+
color: var(--primary);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.slash-item.is-selected::before {
|
|
432
|
+
content: '';
|
|
433
|
+
position: absolute;
|
|
434
|
+
left: 0;
|
|
435
|
+
top: 4px;
|
|
436
|
+
bottom: 4px;
|
|
437
|
+
width: 3px;
|
|
438
|
+
border-radius: 0 2px 2px 0;
|
|
439
|
+
background: var(--primary);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.slash-icon {
|
|
443
|
+
display: inline-flex;
|
|
444
|
+
align-items: center;
|
|
445
|
+
justify-content: center;
|
|
446
|
+
flex-shrink: 0;
|
|
447
|
+
width: 24px;
|
|
448
|
+
height: 24px;
|
|
449
|
+
border-radius: var(--radius-md, 6px);
|
|
450
|
+
background: var(--muted);
|
|
451
|
+
color: var(--muted-foreground);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.slash-item.is-selected .slash-icon,
|
|
455
|
+
.slash-item:hover .slash-icon {
|
|
456
|
+
background: color-mix(in srgb, var(--primary) 14%, transparent);
|
|
457
|
+
color: var(--primary);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.slash-label {
|
|
461
|
+
flex: 1;
|
|
462
|
+
min-width: 0;
|
|
463
|
+
overflow: hidden;
|
|
464
|
+
text-overflow: ellipsis;
|
|
465
|
+
white-space: nowrap;
|
|
466
|
+
}
|
|
467
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Editor } from "@tiptap/core";
|
|
2
|
-
import type { ToolbarFeature } from "../types";
|
|
2
|
+
import type { ToolbarFeature, PromptHandler } from "../types";
|
|
3
3
|
import type { Component } from "svelte";
|
|
4
4
|
type $$ComponentProps = {
|
|
5
5
|
editor: Editor;
|
|
@@ -9,6 +9,8 @@ type $$ComponentProps = {
|
|
|
9
9
|
onPdfUpload?: () => void;
|
|
10
10
|
onFileUpload?: () => void;
|
|
11
11
|
onVideoUpload?: () => void;
|
|
12
|
+
onPromptLink?: PromptHandler;
|
|
13
|
+
onPromptImage?: PromptHandler;
|
|
12
14
|
};
|
|
13
15
|
declare const SlashCommandMenu: Component<$$ComponentProps, {}, "">;
|
|
14
16
|
type SlashCommandMenu = ReturnType<typeof SlashCommandMenu>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlashCommandMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/components/SlashCommandMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAsB3C,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"SlashCommandMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/components/SlashCommandMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAsB3C,OAAO,KAAK,EAAiB,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEvC,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,CAAC;AA8VJ,QAAA,MAAM,gBAAgB,qCAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
style="top: {pos.top}px; left: {pos.left}px; width: {pos.width}px"
|
|
125
125
|
>
|
|
126
126
|
<div
|
|
127
|
-
class="flex items-center justify-center gap-0.5 px-1.5 py-1 bg-foreground rounded-
|
|
127
|
+
class="flex items-center justify-center gap-0.5 px-1.5 py-1 bg-foreground rounded-full shadow-xl w-fit mx-auto"
|
|
128
128
|
>
|
|
129
129
|
<!-- Row/Col buttons -->
|
|
130
130
|
<div class="hce-table-btn-wrap">
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
onclick={() => editor.chain().focus().addRowBefore().run()}
|
|
134
134
|
disabled={!editor.can().addRowBefore()}
|
|
135
135
|
class={cn(
|
|
136
|
-
"p-1.5 rounded-
|
|
136
|
+
"p-1.5 rounded-full transition-colors",
|
|
137
137
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
138
138
|
!editor.can().addRowBefore() && "opacity-30 pointer-events-none",
|
|
139
139
|
)}
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
onclick={() => editor.chain().focus().addRowAfter().run()}
|
|
150
150
|
disabled={!editor.can().addRowAfter()}
|
|
151
151
|
class={cn(
|
|
152
|
-
"p-1.5 rounded-
|
|
152
|
+
"p-1.5 rounded-full transition-colors",
|
|
153
153
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
154
154
|
!editor.can().addRowAfter() && "opacity-30 pointer-events-none",
|
|
155
155
|
)}
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
onclick={() => editor.chain().focus().addColumnBefore().run()}
|
|
166
166
|
disabled={!editor.can().addColumnBefore()}
|
|
167
167
|
class={cn(
|
|
168
|
-
"p-1.5 rounded-
|
|
168
|
+
"p-1.5 rounded-full transition-colors",
|
|
169
169
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
170
170
|
!editor.can().addColumnBefore() &&
|
|
171
171
|
"opacity-30 pointer-events-none",
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
onclick={() => editor.chain().focus().addColumnAfter().run()}
|
|
183
183
|
disabled={!editor.can().addColumnAfter()}
|
|
184
184
|
class={cn(
|
|
185
|
-
"p-1.5 rounded-
|
|
185
|
+
"p-1.5 rounded-full transition-colors",
|
|
186
186
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
187
187
|
!editor.can().addColumnAfter() &&
|
|
188
188
|
"opacity-30 pointer-events-none",
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
onclick={() => editor.chain().focus().toggleHeaderRow().run()}
|
|
202
202
|
disabled={!editor.can().toggleHeaderRow()}
|
|
203
203
|
class={cn(
|
|
204
|
-
"p-1.5 rounded-
|
|
204
|
+
"p-1.5 rounded-full transition-colors",
|
|
205
205
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
206
206
|
!editor.can().toggleHeaderRow() &&
|
|
207
207
|
"opacity-30 pointer-events-none",
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
onclick={() => editor.chain().focus().toggleHeaderColumn().run()}
|
|
219
219
|
disabled={!editor.can().toggleHeaderColumn()}
|
|
220
220
|
class={cn(
|
|
221
|
-
"p-1.5 rounded-
|
|
221
|
+
"p-1.5 rounded-full transition-colors",
|
|
222
222
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
223
223
|
!editor.can().toggleHeaderColumn() &&
|
|
224
224
|
"opacity-30 pointer-events-none",
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
onclick={() => editor.chain().focus().mergeCells().run()}
|
|
236
236
|
disabled={!editor.can().mergeCells()}
|
|
237
237
|
class={cn(
|
|
238
|
-
"p-1.5 rounded-
|
|
238
|
+
"p-1.5 rounded-full transition-colors",
|
|
239
239
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
240
240
|
!editor.can().mergeCells() && "opacity-30 pointer-events-none",
|
|
241
241
|
)}
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
onclick={() => editor.chain().focus().splitCell().run()}
|
|
252
252
|
disabled={!editor.can().splitCell()}
|
|
253
253
|
class={cn(
|
|
254
|
-
"p-1.5 rounded-
|
|
254
|
+
"p-1.5 rounded-full transition-colors",
|
|
255
255
|
"text-white/70 hover:text-white hover:bg-white/10",
|
|
256
256
|
!editor.can().splitCell() && "opacity-30 pointer-events-none",
|
|
257
257
|
)}
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
showLineHeight = !showLineHeight;
|
|
273
273
|
showColors = false;
|
|
274
274
|
}}
|
|
275
|
-
class="p-1.5 rounded-
|
|
275
|
+
class="p-1.5 rounded-full transition-colors text-white/70 hover:text-white hover:bg-white/10"
|
|
276
276
|
>
|
|
277
277
|
<UnfoldVertical size={iconSize} />
|
|
278
278
|
</button>
|
|
@@ -325,6 +325,7 @@
|
|
|
325
325
|
<div class="hce-table-btn-wrap">
|
|
326
326
|
<button
|
|
327
327
|
type="button"
|
|
328
|
+
class="p-1.5 rounded-full transition-colors text-white/70 hover:text-white hover:bg-white/10"
|
|
328
329
|
onclick={() => {
|
|
329
330
|
const { tableNode, tablePos } = findTableNode();
|
|
330
331
|
if (!tableNode || tablePos < 0) return;
|
|
@@ -360,7 +361,6 @@
|
|
|
360
361
|
);
|
|
361
362
|
editor.view.dispatch(tr);
|
|
362
363
|
}}
|
|
363
|
-
class="p-1.5 rounded-md transition-colors text-white/70 hover:text-white hover:bg-white/10"
|
|
364
364
|
>
|
|
365
365
|
<EqualApproximately size={iconSize} />
|
|
366
366
|
</button>
|
|
@@ -376,7 +376,7 @@
|
|
|
376
376
|
showColors = !showColors;
|
|
377
377
|
showLineHeight = false;
|
|
378
378
|
}}
|
|
379
|
-
class="p-1.5 rounded-
|
|
379
|
+
class="p-1.5 rounded-full transition-colors text-white/70 hover:text-white hover:bg-white/10"
|
|
380
380
|
>
|
|
381
381
|
<Paintbrush size={iconSize} />
|
|
382
382
|
</button>
|
|
@@ -388,7 +388,7 @@
|
|
|
388
388
|
style="min-width: 160px"
|
|
389
389
|
>
|
|
390
390
|
<p
|
|
391
|
-
class="text-
|
|
391
|
+
class="text-xs font-medium text-white/60 mb-1.5 px-1"
|
|
392
392
|
>
|
|
393
393
|
배경색
|
|
394
394
|
</p>
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
onclick={() => editor.chain().focus().deleteRow().run()}
|
|
427
427
|
disabled={!editor.can().deleteRow()}
|
|
428
428
|
class={cn(
|
|
429
|
-
"p-1.5 rounded-
|
|
429
|
+
"p-1.5 rounded-full transition-colors",
|
|
430
430
|
"text-white/70 hover:text-red-300 hover:bg-white/10",
|
|
431
431
|
!editor.can().deleteRow() && "opacity-30 pointer-events-none",
|
|
432
432
|
)}
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
onclick={() => editor.chain().focus().deleteColumn().run()}
|
|
443
443
|
disabled={!editor.can().deleteColumn()}
|
|
444
444
|
class={cn(
|
|
445
|
-
"p-1.5 rounded-
|
|
445
|
+
"p-1.5 rounded-full transition-colors",
|
|
446
446
|
"text-white/70 hover:text-red-300 hover:bg-white/10",
|
|
447
447
|
!editor.can().deleteColumn() && "opacity-30 pointer-events-none",
|
|
448
448
|
)}
|
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
onclick={() => editor.chain().focus().deleteTable().run()}
|
|
459
459
|
disabled={!editor.can().deleteTable()}
|
|
460
460
|
class={cn(
|
|
461
|
-
"p-1.5 rounded-
|
|
461
|
+
"p-1.5 rounded-full transition-colors",
|
|
462
462
|
"text-white/70 hover:text-red-300 hover:bg-white/10",
|
|
463
463
|
!editor.can().deleteTable() && "opacity-30 pointer-events-none",
|
|
464
464
|
)}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
import BubbleToolbar from "./BubbleToolbar.svelte";
|
|
42
42
|
import SlashCommandMenu from "./SlashCommandMenu.svelte";
|
|
43
43
|
import TableBubbleMenu from "./TableBubbleMenu.svelte";
|
|
44
|
-
import type { UploadHandler, ToolbarMode, ToolbarFeature } from "../types";
|
|
44
|
+
import type { UploadHandler, PromptHandler, ToolbarMode, ToolbarFeature } from "../types";
|
|
45
45
|
import { resolveFeatures } from "../types";
|
|
46
46
|
import type { FileResolver } from "../extensions/FileAttachment";
|
|
47
47
|
|
|
@@ -113,6 +113,8 @@
|
|
|
113
113
|
placeholder = "'/'를 눌러 명령어를 입력하세요...",
|
|
114
114
|
onUploadFile,
|
|
115
115
|
onResolveFile,
|
|
116
|
+
onPromptLink,
|
|
117
|
+
onPromptImage,
|
|
116
118
|
extensions: extraExtensions = [],
|
|
117
119
|
editable = true,
|
|
118
120
|
toolbar = 'full',
|
|
@@ -123,6 +125,8 @@
|
|
|
123
125
|
placeholder?: string;
|
|
124
126
|
onUploadFile?: UploadHandler;
|
|
125
127
|
onResolveFile?: FileResolver;
|
|
128
|
+
onPromptLink?: PromptHandler;
|
|
129
|
+
onPromptImage?: PromptHandler;
|
|
126
130
|
extensions?: AnyExtension[];
|
|
127
131
|
editable?: boolean;
|
|
128
132
|
toolbar?: ToolbarMode;
|
|
@@ -145,7 +149,7 @@
|
|
|
145
149
|
let slashMenuPos = $state({ top: 0, left: 0 });
|
|
146
150
|
let slashQuery = $state("");
|
|
147
151
|
let slashStartPos: number | null = null;
|
|
148
|
-
const MENU_HEIGHT =
|
|
152
|
+
const MENU_HEIGHT = 320;
|
|
149
153
|
|
|
150
154
|
function updateSlashMenuPosition() {
|
|
151
155
|
if (!editor || slashStartPos === null) return;
|
|
@@ -332,6 +336,8 @@
|
|
|
332
336
|
})]),
|
|
333
337
|
Placeholder.configure({
|
|
334
338
|
placeholder: ({ node }) => {
|
|
339
|
+
// 코드블록·인용·목록 등 컨테이너/의미 있는 노드에는 placeholder 안 띄움
|
|
340
|
+
if (node.type.name === "codeBlock") return "";
|
|
335
341
|
if (node.type.name === "heading") {
|
|
336
342
|
const level = node.attrs.level;
|
|
337
343
|
if (level === 1) return "제목 1";
|
|
@@ -567,6 +573,8 @@
|
|
|
567
573
|
<FixedToolbar
|
|
568
574
|
{editor}
|
|
569
575
|
{features}
|
|
576
|
+
{onPromptLink}
|
|
577
|
+
{onPromptImage}
|
|
570
578
|
onPdfClick={() => pdfInputEl?.click()}
|
|
571
579
|
onFileClick={onUploadFile ? () => fileInputEl?.click() : undefined}
|
|
572
580
|
onVideoClick={onUploadFile ? () => videoInputEl?.click() : undefined}
|
|
@@ -577,7 +585,7 @@
|
|
|
577
585
|
|
|
578
586
|
{#if editor && editable}
|
|
579
587
|
{#if features.has('bubble-toolbar')}
|
|
580
|
-
<BubbleToolbar {editor} {features} />
|
|
588
|
+
<BubbleToolbar {editor} {features} {onPromptLink} />
|
|
581
589
|
{/if}
|
|
582
590
|
|
|
583
591
|
{#if features.has('table-menu')}
|
|
@@ -602,6 +610,8 @@
|
|
|
602
610
|
<SlashCommandMenu
|
|
603
611
|
{editor}
|
|
604
612
|
{features}
|
|
613
|
+
{onPromptLink}
|
|
614
|
+
{onPromptImage}
|
|
605
615
|
query={slashQuery}
|
|
606
616
|
onClose={closeSlashMenu}
|
|
607
617
|
onPdfUpload={onUploadFile && features.has('pdf')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AnyExtension } from "@tiptap/core";
|
|
2
|
-
import type { UploadHandler, ToolbarMode, ToolbarFeature } from "../types";
|
|
2
|
+
import type { UploadHandler, PromptHandler, ToolbarMode, ToolbarFeature } from "../types";
|
|
3
3
|
import type { FileResolver } from "../extensions/FileAttachment";
|
|
4
4
|
type $$ComponentProps = {
|
|
5
5
|
content: string;
|
|
@@ -7,6 +7,8 @@ type $$ComponentProps = {
|
|
|
7
7
|
placeholder?: string;
|
|
8
8
|
onUploadFile?: UploadHandler;
|
|
9
9
|
onResolveFile?: FileResolver;
|
|
10
|
+
onPromptLink?: PromptHandler;
|
|
11
|
+
onPromptImage?: PromptHandler;
|
|
10
12
|
extensions?: AnyExtension[];
|
|
11
13
|
editable?: boolean;
|
|
12
14
|
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,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,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;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 +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,
|
|
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"}
|