Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 0s
206 lines
5.3 KiB
TypeScript
206 lines
5.3 KiB
TypeScript
import {
|
|
CheckSquare,
|
|
Heading2,
|
|
Heading3,
|
|
Heading4,
|
|
ImageIcon,
|
|
Link2Icon,
|
|
List,
|
|
ListOrdered,
|
|
Text,
|
|
TextQuote,
|
|
Youtube,
|
|
} from "lucide-react";
|
|
|
|
import { Command, renderItems, createSuggestionItems } from "novel";
|
|
import { selectionItems } from "../../selector/selection-items";
|
|
import { uploadFile } from "@/server/actions/image";
|
|
|
|
// const items = selectionItems.filter((item) => !item.inline);
|
|
// const defaultSuggestionItems = items.map((item) => (
|
|
// {
|
|
// title: item.name,
|
|
// description: item.name,
|
|
// searchTerms: [item.name],
|
|
// icon: item.icon,
|
|
// command: ({ editor, range }) => {
|
|
// editor.chain().focus().deleteRange(range).toggleNode(item.name).run();
|
|
// },
|
|
// }
|
|
// ));
|
|
|
|
export const suggestionItems = createSuggestionItems([
|
|
{
|
|
title: "Text",
|
|
description: "Just start typing with plain text.",
|
|
searchTerms: ["p", "paragraph"],
|
|
icon: <Text size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.toggleNode("paragraph", "paragraph")
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: "To-do List",
|
|
description: "Track tasks with a to-do list.",
|
|
searchTerms: ["todo", "task", "list", "check", "checkbox"],
|
|
icon: <CheckSquare size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor.chain().focus().deleteRange(range).toggleTaskList().run();
|
|
},
|
|
},
|
|
{
|
|
title: "Heading 2",
|
|
description: "Medium section heading.",
|
|
searchTerms: ["subtitle", "medium"],
|
|
icon: <Heading2 size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode("heading", { level: 2 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: "Heading 3",
|
|
description: "Small section heading.",
|
|
searchTerms: ["subtitle", "small"],
|
|
icon: <Heading3 size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode("heading", { level: 3 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: "Heading 4",
|
|
description: "Big section heading.",
|
|
searchTerms: ["title", "big", "large"],
|
|
icon: <Heading4 size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setNode("heading", { level: 4 })
|
|
.run();
|
|
},
|
|
},
|
|
{
|
|
title: "Bullet List",
|
|
description: "Create a simple bullet list.",
|
|
searchTerms: ["unordered", "point"],
|
|
icon: <List size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
},
|
|
},
|
|
{
|
|
title: "Numbered List",
|
|
description: "Create a list with numbering.",
|
|
searchTerms: ["ordered"],
|
|
icon: <ListOrdered size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
},
|
|
},
|
|
{
|
|
title: "Quote",
|
|
description: "Capture a quote.",
|
|
searchTerms: ["blockquote"],
|
|
icon: <TextQuote size={18} />,
|
|
command: ({ editor, range }) =>
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.toggleNode("paragraph", "paragraph")
|
|
.toggleBlockquote()
|
|
.run(),
|
|
},
|
|
{
|
|
title: "Link Preview",
|
|
description: "Embed a Link Preview.",
|
|
searchTerms: ["link"],
|
|
icon: <Link2Icon size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor.chain().focus().deleteRange(range).createLinkPreview().run();
|
|
},
|
|
},
|
|
|
|
{
|
|
title: "Image",
|
|
description: "Upload an image from your computer.",
|
|
searchTerms: ["photo", "picture", "media"],
|
|
icon: <ImageIcon size={18} />,
|
|
command: ({ editor, range }) => {
|
|
editor.chain().focus().deleteRange(range).run();
|
|
// upload image
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = "image/*";
|
|
input.onchange = async () => {
|
|
if (input.files?.[0]) {
|
|
const file = input.files[0];
|
|
const pos = editor.view.state.selection.from;
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
const url = await uploadFile(formData);
|
|
console.log("URL", url);
|
|
if (!url) return;
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.setImage({ src: url, alt: file.name, title: file.name })
|
|
.run();
|
|
}
|
|
};
|
|
input.click();
|
|
},
|
|
},
|
|
{
|
|
title: "Youtube",
|
|
description: "Embed a Youtube video.",
|
|
searchTerms: ["video", "youtube", "embed"],
|
|
icon: <Youtube size={18} />,
|
|
command: ({ editor, range }) => {
|
|
const videoLink = prompt("Please enter Youtube Video Link");
|
|
//From https://regexr.com/3dj5t
|
|
const ytregex = new RegExp(
|
|
/^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/,
|
|
);
|
|
|
|
if (ytregex.test(String(videoLink))) {
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.deleteRange(range)
|
|
.setYoutubeVideo({
|
|
src: String(videoLink),
|
|
})
|
|
.run();
|
|
} else {
|
|
if (videoLink !== null) {
|
|
alert("Please enter a correct Youtube Video Link");
|
|
}
|
|
}
|
|
},
|
|
},
|
|
]);
|
|
|
|
export const slashCommand = Command.configure({
|
|
suggestion: {
|
|
items: () => suggestionItems,
|
|
render: renderItems,
|
|
},
|
|
});
|