1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

feat: suppot remote image url

This commit is contained in:
songjunxi
2023-10-24 11:25:48 +08:00
parent 116e116332
commit 2b456637e9
8 changed files with 160 additions and 45 deletions

View File

@@ -24,3 +24,11 @@ export function getUrlFromString(str: string) {
return null;
}
}
export function isImageLink(link: string): boolean {
if (!isValidUrl(link)) return false;
const imageExtensions = [".jpg", ".jpeg", ".png", "webp", ".gif", ".bmp"];
const fileExtension = link.substring(link.lastIndexOf(".")).toLowerCase();
return imageExtensions.includes(fileExtension);
}

View File

@@ -10,8 +10,6 @@ import {
ListPlus,
PartyPopper,
PauseCircle,
Pipette,
Repeat,
Scissors,
Wand,
} from "lucide-react";

View File

@@ -28,8 +28,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({
className="novel-flex novel-h-full novel-items-center novel-space-x-2 novel-px-3 novel-py-1.5 novel-text-sm novel-font-medium novel-text-stone-600 hover:novel-bg-stone-100 active:novel-bg-stone-200"
onClick={() => {
setIsOpen(!isOpen);
}}
>
}}>
<p className="novel-text-base"></p>
<p
className={cn(
@@ -37,8 +36,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({
{
"novel-text-blue-500": editor.isActive("link"),
}
)}
>
)}>
Link
</p>
</button>
@@ -51,8 +49,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({
url && editor.chain().focus().setLink({ href: url }).run();
setIsOpen(false);
}}
className="novel-fixed novel-top-full novel-z-[99999] novel-mt-1 novel-flex novel-w-60 novel-overflow-hidden novel-rounded novel-border novel-border-stone-200 novel-bg-white novel-p-1 novel-shadow-xl novel-animate-in novel-fade-in novel-slide-in-from-top-1"
>
className="novel-fixed novel-top-full novel-z-[99999] novel-mt-1 novel-flex novel-w-60 novel-overflow-hidden novel-rounded novel-border novel-border-stone-200 novel-bg-white novel-p-1 novel-shadow-xl novel-animate-in novel-fade-in novel-slide-in-from-top-1">
<input
ref={inputRef}
type="text"
@@ -67,8 +64,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({
onClick={() => {
editor.chain().focus().unsetLink().run();
setIsOpen(false);
}}
>
}}>
<Trash className="novel-h-4 novel-w-4" />
</button>
) : (

View File

@@ -26,6 +26,7 @@ import {
CheckSquare,
Table2,
PauseCircle,
FileImage,
} from "lucide-react";
import { LoadingCircle } from "@/ui/icons";
import { toast } from "sonner";
@@ -35,6 +36,7 @@ import { getPrevText } from "@/lib/editor";
import { startImageUpload } from "@/ui/editor/plugins/upload-images";
import { NovelContext } from "../provider";
import { Youtube } from "lucide-react";
import { isImageLink } from "@/lib/utils";
interface CommandItemProps {
title: string;
@@ -211,10 +213,10 @@ const getSuggestionItems = ({
},
},
{
title: "Image",
title: "Local image",
description: "Upload an image from your computer.",
searchTerms: ["photo", "picture", "media"],
icon: <ImageIcon size={18} />,
searchTerms: ["photo", "picture", "media", "img"],
icon: <FileImage size={18} />,
command: ({ editor, range }: CommandProps) => {
editor.chain().focus().deleteRange(range).run();
// upload image
@@ -231,6 +233,25 @@ const getSuggestionItems = ({
input.click();
},
},
{
title: "Remote image",
description: "Render an image from url.",
searchTerms: ["photo", "picture", "media", "img"],
icon: <ImageIcon size={18} />,
command: ({ editor, range }: CommandProps) => {
const url = prompt("Enter image url");
if (url && isImageLink(url)) {
editor
.chain()
.focus()
.deleteRange(range)
.setImage({
src: url,
})
.run();
}
},
},
{
title: "Youtube video",
description: "Play the Youtube video you filled out.",

View File

@@ -2,8 +2,6 @@ import { BlobResult } from "@vercel/blob";
import { toast } from "sonner";
import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state";
import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view";
import { NovelContext } from "../provider";
import { useContext } from "react";
const uploadKey = new PluginKey("upload-image");
@@ -26,7 +24,7 @@ const UploadImagesPlugin = () =>
const image = document.createElement("img");
image.setAttribute(
"class",
"opacity-40 rounded-lg border border-stone-200"
"opacity-40 rounded-md border border-stone-200"
);
image.src = src;
placeholder.appendChild(image);
@@ -62,8 +60,8 @@ export function startImageUpload(file: File, view: EditorView, pos: number) {
if (!file.type.includes("image/")) {
toast.error("File type not supported.");
return;
} else if (file.size / 1024 / 1024 > 5) {
toast.error(`File size too big (max ${15}MB).`);
} else if (file.size / 1024 / 1024 > 1) {
toast.error(`File size too big (max ${1}MB).`);
return;
}
@@ -111,7 +109,6 @@ export function startImageUpload(file: File, view: EditorView, pos: number) {
}
export const handleImageUpload = (file: File) => {
// upload to Vercel Blob
return new Promise((resolve) => {
toast.promise(
fetch("/api/upload", {
@@ -125,6 +122,7 @@ export const handleImageUpload = (file: File) => {
// Successfully uploaded image
if (res.status === 200) {
const { url } = (await res.json()) as BlobResult;
// preload the image
let image = new Image();
image.src = url;
@@ -134,7 +132,6 @@ export const handleImageUpload = (file: File) => {
// No blob store configured
} else if (res.status === 401) {
resolve(file);
throw new Error(
"`BLOB_READ_WRITE_TOKEN` environment variable not found, reading image locally instead."
);