@intinyagroup/rich-text 0.1.1-alpha.19 → 0.1.1-alpha.20
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.
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount, onDestroy } from "svelte";
|
|
3
|
-
import { Editor, Node, mergeAttributes } from "@tiptap/core";
|
|
3
|
+
import { Editor, Mark, Node, mergeAttributes } from "@tiptap/core";
|
|
4
4
|
import StarterKit from "@tiptap/starter-kit";
|
|
5
|
+
import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
|
|
6
|
+
import { common, createLowlight } from "lowlight";
|
|
5
7
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
6
8
|
import Underline from "@tiptap/extension-underline";
|
|
7
9
|
import TextAlign from "@tiptap/extension-text-align";
|
|
@@ -48,10 +50,15 @@
|
|
|
48
50
|
Sparkles,
|
|
49
51
|
HelpCircle,
|
|
50
52
|
FileText,
|
|
53
|
+
Search,
|
|
54
|
+
Download,
|
|
55
|
+
Unlink,
|
|
51
56
|
} from "lucide-svelte";
|
|
52
|
-
import { Button, Separator } from "@intinyagroup/ui";
|
|
57
|
+
import { Button, Input, Separator } from "@intinyagroup/ui";
|
|
53
58
|
import { cn } from "@intinyagroup/grid-core/utils";
|
|
54
59
|
|
|
60
|
+
const lowlight = createLowlight(common);
|
|
61
|
+
|
|
55
62
|
let {
|
|
56
63
|
content = "",
|
|
57
64
|
placeholder = "Start writing...",
|
|
@@ -62,6 +69,8 @@
|
|
|
62
69
|
onUpdate,
|
|
63
70
|
onImageUpload,
|
|
64
71
|
onOpenSubPage,
|
|
72
|
+
onExportHtml,
|
|
73
|
+
onExportMarkdown,
|
|
65
74
|
}: {
|
|
66
75
|
content?: string;
|
|
67
76
|
placeholder?: string;
|
|
@@ -74,6 +83,10 @@
|
|
|
74
83
|
/** Called when paste/drop provides an image file. Return a URL to insert. */
|
|
75
84
|
onImageUpload?: (file: File) => Promise<string>;
|
|
76
85
|
onOpenSubPage?: (page: { id: string; title: string }) => void;
|
|
86
|
+
/** Called by HTML export action with current document HTML. */
|
|
87
|
+
onExportHtml?: (html: string) => void;
|
|
88
|
+
/** Called by Markdown export action with best-effort CommonMark output. */
|
|
89
|
+
onExportMarkdown?: (markdown: string) => void;
|
|
77
90
|
} = $props();
|
|
78
91
|
|
|
79
92
|
let editorEl: HTMLDivElement | null = null;
|
|
@@ -81,6 +94,41 @@
|
|
|
81
94
|
let editor: Editor | null = null;
|
|
82
95
|
let isActive = $state<Record<string, boolean>>({});
|
|
83
96
|
let uploadingCount = $state(0);
|
|
97
|
+
let showFindReplace = $state(false);
|
|
98
|
+
let findQuery = $state("");
|
|
99
|
+
let replaceQuery = $state("");
|
|
100
|
+
let findCount = $state(0);
|
|
101
|
+
|
|
102
|
+
const TextColor = Mark.create({
|
|
103
|
+
name: "textColor",
|
|
104
|
+
addAttributes() {
|
|
105
|
+
return { color: { default: null } };
|
|
106
|
+
},
|
|
107
|
+
parseHTML() {
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
tag: "span[data-text-color]",
|
|
111
|
+
getAttrs: (element) => ({
|
|
112
|
+
color:
|
|
113
|
+
(element as HTMLElement).style.color ||
|
|
114
|
+
element.getAttribute("color") ||
|
|
115
|
+
null,
|
|
116
|
+
}),
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
},
|
|
120
|
+
renderHTML({ HTMLAttributes }) {
|
|
121
|
+
const color = HTMLAttributes.color;
|
|
122
|
+
return [
|
|
123
|
+
"span",
|
|
124
|
+
mergeAttributes({
|
|
125
|
+
"data-text-color": "",
|
|
126
|
+
style: color ? `color: ${color}` : undefined,
|
|
127
|
+
}),
|
|
128
|
+
0,
|
|
129
|
+
];
|
|
130
|
+
},
|
|
131
|
+
});
|
|
84
132
|
|
|
85
133
|
// Slash commands state
|
|
86
134
|
let showSlashMenu = $state(false);
|
|
@@ -88,6 +136,10 @@
|
|
|
88
136
|
let slashIndex = $state(0);
|
|
89
137
|
let slashMenuPos = $state({ top: 0, left: 0 });
|
|
90
138
|
|
|
139
|
+
let textColor = $state("#1f2937");
|
|
140
|
+
let highlightColor = $state("#fef08a");
|
|
141
|
+
let searchMatches: Array<{ from: number; to: number }> = [];
|
|
142
|
+
|
|
91
143
|
const slashCommands = [
|
|
92
144
|
{
|
|
93
145
|
title: "Sub-page",
|
|
@@ -324,36 +376,6 @@
|
|
|
324
376
|
}
|
|
325
377
|
}
|
|
326
378
|
|
|
327
|
-
const CustomYoutube = Node.create({
|
|
328
|
-
name: "youtube",
|
|
329
|
-
group: "block",
|
|
330
|
-
atom: true,
|
|
331
|
-
addAttributes() {
|
|
332
|
-
return {
|
|
333
|
-
src: { default: null },
|
|
334
|
-
};
|
|
335
|
-
},
|
|
336
|
-
parseHTML() {
|
|
337
|
-
return [{ tag: 'iframe[src*="youtube"]' }];
|
|
338
|
-
},
|
|
339
|
-
renderHTML({ HTMLAttributes }) {
|
|
340
|
-
return [
|
|
341
|
-
"div",
|
|
342
|
-
{
|
|
343
|
-
class: "video-container my-4 aspect-video rounded-xl overflow-hidden",
|
|
344
|
-
},
|
|
345
|
-
[
|
|
346
|
-
"iframe",
|
|
347
|
-
mergeAttributes(HTMLAttributes, {
|
|
348
|
-
class: "w-full h-full border-0",
|
|
349
|
-
allow:
|
|
350
|
-
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
351
|
-
allowfullscreen: "true",
|
|
352
|
-
}),
|
|
353
|
-
],
|
|
354
|
-
];
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
379
|
/** SubPage block extension (Page-in-page card) */
|
|
358
380
|
const SubPage = Node.create({
|
|
359
381
|
name: "subpage",
|
|
@@ -452,6 +474,7 @@
|
|
|
452
474
|
underline: e.isActive("underline"),
|
|
453
475
|
strike: e.isActive("strike"),
|
|
454
476
|
highlight: e.isActive("highlight"),
|
|
477
|
+
textColor: e.isActive("textColor"),
|
|
455
478
|
h1: e.isActive("heading", { level: 1 }),
|
|
456
479
|
h2: e.isActive("heading", { level: 2 }),
|
|
457
480
|
h3: e.isActive("heading", { level: 3 }),
|
|
@@ -467,6 +490,115 @@
|
|
|
467
490
|
};
|
|
468
491
|
}
|
|
469
492
|
|
|
493
|
+
function setTextColor(color: string) {
|
|
494
|
+
textColor = color;
|
|
495
|
+
editor?.chain().focus().setMark("textColor", { color }).run();
|
|
496
|
+
}
|
|
497
|
+
function toggleTextColor() {
|
|
498
|
+
if (editor?.isActive("textColor"))
|
|
499
|
+
editor.chain().focus().unsetMark("textColor").run();
|
|
500
|
+
else setTextColor(textColor);
|
|
501
|
+
}
|
|
502
|
+
function toggleHighlight(color = highlightColor) {
|
|
503
|
+
highlightColor = color;
|
|
504
|
+
editor?.chain().focus().toggleHighlight({ color }).run();
|
|
505
|
+
}
|
|
506
|
+
function updateSearchMatches() {
|
|
507
|
+
searchMatches = [];
|
|
508
|
+
if (!editor || !findQuery) {
|
|
509
|
+
findCount = 0;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const query = findQuery.toLocaleLowerCase();
|
|
513
|
+
editor.state.doc.descendants((node, pos) => {
|
|
514
|
+
if (!node.isText || !node.text) return;
|
|
515
|
+
let index = 0;
|
|
516
|
+
const text = node.text.toLocaleLowerCase();
|
|
517
|
+
while ((index = text.indexOf(query, index)) !== -1) {
|
|
518
|
+
searchMatches.push({
|
|
519
|
+
from: pos + 1 + index,
|
|
520
|
+
to: pos + 1 + index + query.length,
|
|
521
|
+
});
|
|
522
|
+
index += query.length;
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
findCount = searchMatches.length;
|
|
526
|
+
}
|
|
527
|
+
function findNext() {
|
|
528
|
+
updateSearchMatches();
|
|
529
|
+
if (!editor || !searchMatches.length) return;
|
|
530
|
+
const current = editor.state.selection.from;
|
|
531
|
+
const next =
|
|
532
|
+
searchMatches.find((match) => match.from > current) ?? searchMatches[0];
|
|
533
|
+
editor.chain().focus().setTextSelection(next).run();
|
|
534
|
+
}
|
|
535
|
+
function replaceCurrent() {
|
|
536
|
+
if (!editor || !findQuery) return;
|
|
537
|
+
const current = editor.state.selection;
|
|
538
|
+
if (
|
|
539
|
+
current.from !== current.to &&
|
|
540
|
+
editor.state.doc.textBetween(current.from, current.to) === findQuery
|
|
541
|
+
) {
|
|
542
|
+
editor
|
|
543
|
+
.chain()
|
|
544
|
+
.focus()
|
|
545
|
+
.insertContentAt({ from: current.from, to: current.to }, replaceQuery)
|
|
546
|
+
.run();
|
|
547
|
+
} else findNext();
|
|
548
|
+
updateSearchMatches();
|
|
549
|
+
}
|
|
550
|
+
function replaceAll() {
|
|
551
|
+
updateSearchMatches();
|
|
552
|
+
if (!editor || !searchMatches.length) return;
|
|
553
|
+
const transaction = editor.state.tr;
|
|
554
|
+
for (const match of [...searchMatches].reverse())
|
|
555
|
+
transaction.insertText(replaceQuery, match.from, match.to);
|
|
556
|
+
editor.view.dispatch(transaction);
|
|
557
|
+
updateSearchMatches();
|
|
558
|
+
}
|
|
559
|
+
function markdownFromHtml(html: string) {
|
|
560
|
+
return html
|
|
561
|
+
.replace(/<br\s*\/?>(\n)?/gi, "\n")
|
|
562
|
+
.replace(
|
|
563
|
+
/<h([1-6])>(.*?)<\/h\1>/gi,
|
|
564
|
+
(_m, level, text) => `${"#".repeat(Number(level))} ${text}\n\n`,
|
|
565
|
+
)
|
|
566
|
+
.replace(/<strong>(.*?)<\/strong>/gi, "**$1**")
|
|
567
|
+
.replace(/<em>(.*?)<\/em>/gi, "_$1_")
|
|
568
|
+
.replace(/<u>(.*?)<\/u>/gi, "__$1__")
|
|
569
|
+
.replace(/<s>(.*?)<\/s>/gi, "~~$1~~")
|
|
570
|
+
.replace(/<a[^>]+href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gi, "[$2]($1)")
|
|
571
|
+
.replace(/<li>(.*?)<\/li>/gi, "- $1\n")
|
|
572
|
+
.replace(/<p>(.*?)<\/p>/gi, "$1\n\n")
|
|
573
|
+
.replace(/<hr\s*\/?>(\n)?/gi, "---\n\n")
|
|
574
|
+
.replace(/<[^>]+>/g, "")
|
|
575
|
+
.replace(/ /g, " ")
|
|
576
|
+
.replace(/&/g, "&")
|
|
577
|
+
.replace(/</g, "<")
|
|
578
|
+
.replace(/>/g, ">")
|
|
579
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
580
|
+
.trim();
|
|
581
|
+
}
|
|
582
|
+
function downloadText(filename: string, content: string, type: string) {
|
|
583
|
+
const url = URL.createObjectURL(new Blob([content], { type }));
|
|
584
|
+
const link = document.createElement("a");
|
|
585
|
+
link.href = url;
|
|
586
|
+
link.download = filename;
|
|
587
|
+
link.click();
|
|
588
|
+
URL.revokeObjectURL(url);
|
|
589
|
+
}
|
|
590
|
+
function exportHtml() {
|
|
591
|
+
if (!editor) return;
|
|
592
|
+
const html = editor.getHTML();
|
|
593
|
+
onExportHtml?.(html);
|
|
594
|
+
downloadText("document.html", html, "text/html;charset=utf-8");
|
|
595
|
+
}
|
|
596
|
+
function exportMarkdown() {
|
|
597
|
+
if (!editor) return;
|
|
598
|
+
const markdown = markdownFromHtml(editor.getHTML());
|
|
599
|
+
onExportMarkdown?.(markdown);
|
|
600
|
+
downloadText("document.md", markdown, "text/markdown;charset=utf-8");
|
|
601
|
+
}
|
|
470
602
|
function toggleBold() {
|
|
471
603
|
editor?.chain().focus().toggleBold().run();
|
|
472
604
|
}
|
|
@@ -497,10 +629,6 @@
|
|
|
497
629
|
function setAlign(align: "left" | "center" | "right" | "justify") {
|
|
498
630
|
editor?.chain().focus().setTextAlign(align).run();
|
|
499
631
|
}
|
|
500
|
-
function setLink() {
|
|
501
|
-
const url = window.prompt("Enter URL:");
|
|
502
|
-
if (url) editor?.chain().focus().setLink({ href: url }).run();
|
|
503
|
-
}
|
|
504
632
|
function setImage() {
|
|
505
633
|
const url = window.prompt("Enter image URL:");
|
|
506
634
|
if (url) editor?.chain().focus().setImage({ src: url }).run();
|
|
@@ -552,6 +680,19 @@
|
|
|
552
680
|
cmd.action();
|
|
553
681
|
showSlashMenu = false;
|
|
554
682
|
}
|
|
683
|
+
function setLink() {
|
|
684
|
+
if (!editor) return;
|
|
685
|
+
if (editor.isActive("link")) {
|
|
686
|
+
const current = editor.getAttributes("link").href || "";
|
|
687
|
+
const url = window.prompt("Edit URL:", current);
|
|
688
|
+
if (url === null) return;
|
|
689
|
+
if (!url.trim()) editor.chain().focus().unsetLink().run();
|
|
690
|
+
else editor.chain().focus().setLink({ href: url.trim() }).run();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const url = window.prompt("Enter URL:");
|
|
694
|
+
if (url?.trim()) editor.chain().focus().setLink({ href: url.trim() }).run();
|
|
695
|
+
}
|
|
555
696
|
function addColumnBefore() {
|
|
556
697
|
editor?.chain().focus().addColumnBefore().run();
|
|
557
698
|
}
|
|
@@ -590,9 +731,11 @@
|
|
|
590
731
|
editor = new Editor({
|
|
591
732
|
element: editorEl,
|
|
592
733
|
extensions: [
|
|
593
|
-
StarterKit,
|
|
734
|
+
StarterKit.configure({ codeBlock: false }),
|
|
735
|
+
CodeBlockLowlight.configure({ lowlight }),
|
|
594
736
|
Placeholder.configure({ placeholder }),
|
|
595
737
|
Underline,
|
|
738
|
+
TextColor,
|
|
596
739
|
TextAlign.configure({ types: ["heading", "paragraph"] }),
|
|
597
740
|
Link.configure({ openOnClick: false }),
|
|
598
741
|
CustomImage.configure({
|
|
@@ -908,6 +1051,43 @@
|
|
|
908
1051
|
>
|
|
909
1052
|
<Highlighter class="size-3.5" />
|
|
910
1053
|
</Button>
|
|
1054
|
+
<label
|
|
1055
|
+
class="relative flex size-7.5 cursor-pointer items-center justify-center rounded-md text-[var(--ui-muted-foreground)] hover:bg-[var(--ui-secondary)] hover:text-[var(--ui-foreground)] focus-within:ring-2 focus-within:ring-[var(--ui-ring)]"
|
|
1056
|
+
title="Text color"
|
|
1057
|
+
>
|
|
1058
|
+
<span class="text-xs font-bold" style="color: {textColor}">A</span>
|
|
1059
|
+
<input
|
|
1060
|
+
class="absolute inset-0 cursor-pointer opacity-0"
|
|
1061
|
+
type="color"
|
|
1062
|
+
value={textColor}
|
|
1063
|
+
aria-label="Text color"
|
|
1064
|
+
onchange={(event) =>
|
|
1065
|
+
setTextColor((event.currentTarget as HTMLInputElement).value)}
|
|
1066
|
+
/>
|
|
1067
|
+
</label>
|
|
1068
|
+
<label
|
|
1069
|
+
class="relative flex size-7.5 cursor-pointer items-center justify-center rounded-md text-[var(--ui-muted-foreground)] hover:bg-[var(--ui-secondary)] hover:text-[var(--ui-foreground)] hover:text-[var(--ui-foreground)] focus-within:ring-2 focus-within:ring-[var(--ui-ring)]"
|
|
1070
|
+
title="Highlight color"
|
|
1071
|
+
>
|
|
1072
|
+
<Highlighter class="size-3.5" style="color: {highlightColor}" />
|
|
1073
|
+
<input
|
|
1074
|
+
class="absolute inset-0 cursor-pointer opacity-0"
|
|
1075
|
+
type="color"
|
|
1076
|
+
value={highlightColor}
|
|
1077
|
+
aria-label="Highlight color"
|
|
1078
|
+
onchange={(event) =>
|
|
1079
|
+
toggleHighlight((event.currentTarget as HTMLInputElement).value)}
|
|
1080
|
+
/>
|
|
1081
|
+
</label>
|
|
1082
|
+
<Button
|
|
1083
|
+
variant="ghost"
|
|
1084
|
+
size="sm"
|
|
1085
|
+
class="size-7.5 p-0 rounded-md text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)]"
|
|
1086
|
+
onclick={toggleTextColor}
|
|
1087
|
+
title="Toggle text color"
|
|
1088
|
+
>
|
|
1089
|
+
<span class="text-xs font-bold">A</span>
|
|
1090
|
+
</Button>
|
|
911
1091
|
</div>
|
|
912
1092
|
|
|
913
1093
|
<Separator orientation="vertical" class="h-4 mx-1.5 opacity-60" />
|
|
@@ -1048,6 +1228,46 @@
|
|
|
1048
1228
|
>
|
|
1049
1229
|
<LinkIcon class="size-3.5" />
|
|
1050
1230
|
</Button>
|
|
1231
|
+
{#if isActive.link}
|
|
1232
|
+
<Button
|
|
1233
|
+
variant="ghost"
|
|
1234
|
+
size="sm"
|
|
1235
|
+
class="size-7.5 p-0 rounded-md text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)]"
|
|
1236
|
+
onclick={() => editor?.chain().focus().unsetLink().run()}
|
|
1237
|
+
title="Remove link"
|
|
1238
|
+
>
|
|
1239
|
+
<Unlink class="size-3.5" />
|
|
1240
|
+
</Button>
|
|
1241
|
+
{/if}
|
|
1242
|
+
<Button
|
|
1243
|
+
variant="ghost"
|
|
1244
|
+
size="sm"
|
|
1245
|
+
class="size-7.5 p-0 rounded-md text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)]"
|
|
1246
|
+
onclick={() => (showFindReplace = !showFindReplace)}
|
|
1247
|
+
title="Find and replace"
|
|
1248
|
+
>
|
|
1249
|
+
<Search class="size-3.5" />
|
|
1250
|
+
</Button>
|
|
1251
|
+
<Button
|
|
1252
|
+
variant="ghost"
|
|
1253
|
+
size="sm"
|
|
1254
|
+
class="size-7.5 p-0 rounded-md text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)]"
|
|
1255
|
+
onclick={exportHtml}
|
|
1256
|
+
title="Export HTML"
|
|
1257
|
+
>
|
|
1258
|
+
<Download class="size-3.5" /><span class="sr-only">Export HTML</span>
|
|
1259
|
+
</Button>
|
|
1260
|
+
<Button
|
|
1261
|
+
variant="ghost"
|
|
1262
|
+
size="sm"
|
|
1263
|
+
class="hidden size-7.5 p-0 rounded-md text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)] hover:bg-[var(--ui-secondary)] sm:inline-flex"
|
|
1264
|
+
onclick={exportMarkdown}
|
|
1265
|
+
title="Export Markdown"
|
|
1266
|
+
>
|
|
1267
|
+
<FileText class="size-3.5" /><span class="sr-only"
|
|
1268
|
+
>Export Markdown</span
|
|
1269
|
+
>
|
|
1270
|
+
</Button>
|
|
1051
1271
|
<Button
|
|
1052
1272
|
variant="ghost"
|
|
1053
1273
|
size="sm"
|
|
@@ -1094,6 +1314,39 @@
|
|
|
1094
1314
|
{/if}
|
|
1095
1315
|
</div>
|
|
1096
1316
|
{/if}
|
|
1317
|
+
{#if editable && mode === "classic" && showFindReplace}
|
|
1318
|
+
<div
|
|
1319
|
+
class="flex flex-wrap items-center gap-2 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/30 px-3 py-2"
|
|
1320
|
+
role="search"
|
|
1321
|
+
aria-label="Find and replace"
|
|
1322
|
+
>
|
|
1323
|
+
<Input
|
|
1324
|
+
class="h-8 min-w-32 flex-1 sm:max-w-56"
|
|
1325
|
+
bind:value={findQuery}
|
|
1326
|
+
placeholder="Find text"
|
|
1327
|
+
aria-label="Find text"
|
|
1328
|
+
oninput={updateSearchMatches}
|
|
1329
|
+
onkeydown={(event) => event.key === "Enter" && findNext()}
|
|
1330
|
+
/>
|
|
1331
|
+
<Input
|
|
1332
|
+
class="h-8 min-w-32 flex-1 sm:max-w-56"
|
|
1333
|
+
bind:value={replaceQuery}
|
|
1334
|
+
placeholder="Replace with"
|
|
1335
|
+
aria-label="Replace with"
|
|
1336
|
+
/>
|
|
1337
|
+
<span
|
|
1338
|
+
class="text-xs tabular-nums text-[var(--ui-muted-foreground)]"
|
|
1339
|
+
aria-live="polite">{findCount} match{findCount === 1 ? "" : "es"}</span
|
|
1340
|
+
>
|
|
1341
|
+
<Button variant="outline" size="sm" onclick={findNext}>Find next</Button>
|
|
1342
|
+
<Button variant="outline" size="sm" onclick={replaceCurrent}
|
|
1343
|
+
>Replace</Button
|
|
1344
|
+
>
|
|
1345
|
+
<Button variant="outline" size="sm" onclick={replaceAll}
|
|
1346
|
+
>Replace all</Button
|
|
1347
|
+
>
|
|
1348
|
+
</div>
|
|
1349
|
+
{/if}
|
|
1097
1350
|
<div
|
|
1098
1351
|
bind:this={editorEl}
|
|
1099
1352
|
class="rich-text-content prose prose-sm max-w-none p-5 sm:p-7 focus:outline-none"
|
|
@@ -13,6 +13,10 @@ type $$ComponentProps = {
|
|
|
13
13
|
id: string;
|
|
14
14
|
title: string;
|
|
15
15
|
}) => void;
|
|
16
|
+
/** Called by HTML export action with current document HTML. */
|
|
17
|
+
onExportHtml?: (html: string) => void;
|
|
18
|
+
/** Called by Markdown export action with best-effort CommonMark output. */
|
|
19
|
+
onExportMarkdown?: (markdown: string) => void;
|
|
16
20
|
};
|
|
17
21
|
declare const RichTextEditor: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
18
22
|
type RichTextEditor = ReturnType<typeof RichTextEditor>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intinyagroup/rich-text",
|
|
3
|
-
"version": "0.1.1-alpha.
|
|
3
|
+
"version": "0.1.1-alpha.20",
|
|
4
4
|
"description": "Rich text editor component powered by TipTap",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"dev": "svelte-package -o dist -w"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@intinyagroup/grid-core": "0.1.1-alpha.
|
|
25
|
-
"@intinyagroup/ui": "0.1.1-alpha.
|
|
24
|
+
"@intinyagroup/grid-core": "0.1.1-alpha.20",
|
|
25
|
+
"@intinyagroup/ui": "0.1.1-alpha.20",
|
|
26
26
|
"@tiptap/core": "^3.22.5",
|
|
27
27
|
"@tiptap/extension-bubble-menu": "^3.22.5",
|
|
28
28
|
"@tiptap/extension-code-block-lowlight": "^3.22.5",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@tiptap/extension-underline": "^3.22.5",
|
|
42
42
|
"@tiptap/pm": "^3.22.5",
|
|
43
43
|
"@tiptap/starter-kit": "^3.22.5",
|
|
44
|
+
"lowlight": "^3.3.0",
|
|
44
45
|
"lucide-svelte": "^1.0.1"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|