steven-tey / novel

Notion-style WYSIWYG editor with AI-powered autocompletion.
https://novel.sh
Apache License 2.0
12.7k stars 1.04k forks source link

Added Basic Youtube Embed feature + Word Counter #383

Closed MODSetter closed 5 months ago

MODSetter commented 5 months ago

Added basic Youtube Embed Feature + Word Counter

Demo:

https://github.com/steven-tey/novel/assets/122026167/4b803d57-3a8f-4c98-b065-a57a2b7f5439

vercel[bot] commented 5 months ago

@MODSetter is attempting to deploy a commit to the Dub Team on Vercel.

A member of the Team first needs to authorize it.

socket-security[bot] commented 5 months ago

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/@radix-ui/react-dropdown-menu@2.0.6 None 0 145 kB benoitgrelard
npm/@tailwindcss/typography@0.5.12 None 0 110 kB adamwathan
npm/use-debounce@9.0.4 None 0 106 kB xnimorz

🚮 Removed packages: npm/typescript@5.4.5

View full report↗︎

lumpinif commented 5 months ago

TypeError: editor.chain(...).focus(...).deleteRange(...).setYoutubeVideo is not a function

MODSetter commented 5 months ago

TypeError: editor.chain(...).focus(...).deleteRange(...).setYoutubeVideo is not a function

Do Build of headless package first

lumpinif commented 5 months ago

what if i dont want to wait and include headless package in my project, I have tried to add youtube directly from tiptap, but it failed to run the command.

the command from tiptap for youtube is like "editor.commands.setYoutubeVideo({ src: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', width: 640, height: 480, })" which is also not working in slash-command.tsx

MODSetter commented 5 months ago

what if i dont want to wait and include headless package in my project, I have tried to add youtube directly from tiptap, but it failed to run the command.

the command from tiptap for youtube is like "editor.commands.setYoutubeVideo({ src: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', width: 640, height: 480, })" which is also not working in slash-command.tsx

I tried with direct configuration before to test the impl then made the final changes in package. You must be not configuring it properly. See tiptap docs it helped me. Make sure you are loading youtube extension in editor.

lumpinif commented 5 months ago

what if i dont want to wait and include headless package in my project, I have tried to add youtube directly from tiptap, but it failed to run the command. the command from tiptap for youtube is like "editor.commands.setYoutubeVideo({ src: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', width: 640, height: 480, })" which is also not working in slash-command.tsx

I tried with direct configuration before to test the impl then made the final changes in package. You must be not configuring it properly. See tiptap docs it helped me. Make sure you are loading youtube extension in editor.

did you mean you tried with direct implementation and it worked? If so how different you configured it by directly implementing

MODSetter commented 5 months ago

what if i dont want to wait and include headless package in my project, I have tried to add youtube directly from tiptap, but it failed to run the command. the command from tiptap for youtube is like "editor.commands.setYoutubeVideo({ src: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', width: 640, height: 480, })" which is also not working in slash-command.tsx

I tried with direct configuration before to test the impl then made the final changes in package. You must be not configuring it properly. See tiptap docs it helped me. Make sure you are loading youtube extension in editor.

did you mean you tried with direct implementation and it worked? If so how different you configured it by directly implementing

On Mobile so I don't remember every file name but I think you load ,configure and export in extension.ts and it should work.

lumpinif commented 5 months ago
image image

I build another selector to add the youtube video , and it words, but I don't know if I can get the slash-command right though. I will try again.

happy to share the youtube-selector here :

`import { useEffect, useRef } from "react" import { Check, Trash } from "lucide-react" import { useEditor } from "novel"

import { cn, isValidUrl, isValidYoutubeUrl } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"

interface YoutubeSelectorProps { open: boolean onOpenChange: (open: boolean) => void }

function getYoutubeUrlFromString(str: string) { if (isValidUrl(str)) { if (isValidYoutubeUrl(str)) { return str } } try { if (str.includes(".") && !str.includes(" ")) { const url = new URL(https://${str}) if (isValidYoutubeUrl(url.toString())) { return url.toString() } } } catch (e) { return null } return null }

export const YoutubeSelector = ({ open, onOpenChange, }: YoutubeSelectorProps) => { const inputRef = useRef(null) const widthRef = useRef(null) const heightRef = useRef(null) const { editor } = useEditor()

// Autofocus on input by default useEffect(() => { inputRef.current && inputRef.current?.focus() })

if (!editor) return null

const addYoutubeVideo = () => { const url = getYoutubeUrlFromString(inputRef.current?.value || "")

if (url) {
  editor.commands.setYoutubeVideo({
    src: url,
    width: Math.max(320, parseInt(widthRef.current?.value || "640", 10)),
    height: Math.max(180, parseInt(heightRef.current?.value || "480", 10)),
  })
  onOpenChange(false)
} else {
  alert("Please enter a valid YouTube URL")
}

}

return (

) } ` Hey thanks man @MODSetter
MODSetter commented 5 months ago

image image I build another selector to add the youtube video , and it words, but I don't know if I can get the slash-command right though. I will try again.

happy to share the youtube-selector here :

`import { useEffect, useRef } from "react" import { Check, Trash } from "lucide-react" import { useEditor } from "novel"

import { cn, isValidUrl, isValidYoutubeUrl } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"

interface YoutubeSelectorProps { open: boolean onOpenChange: (open: boolean) => void }

function getYoutubeUrlFromString(str: string) { if (isValidUrl(str)) { if (isValidYoutubeUrl(str)) { return str } } try { if (str.includes(".") && !str.includes(" ")) { const url = new URL(https://${str}) if (isValidYoutubeUrl(url.toString())) { return url.toString() } } } catch (e) { return null } return null }

export const YoutubeSelector = ({ open, onOpenChange, }: YoutubeSelectorProps) => { const inputRef = useRef(null) const widthRef = useRef(null) const heightRef = useRef(null) const { editor } = useEditor()

// Autofocus on input by default useEffect(() => { inputRef.current && inputRef.current?.focus() })

if (!editor) return null

const addYoutubeVideo = () => { const url = getYoutubeUrlFromString(inputRef.current?.value || "")

if (url) {
  editor.commands.setYoutubeVideo({
    src: url,
    width: Math.max(320, parseInt(widthRef.current?.value || "640", 10)),
    height: Math.max(180, parseInt(heightRef.current?.value || "480", 10)),
  })
  onOpenChange(false)
} else {
  alert("Please enter a valid YouTube URL")
}

}

return (

<p className={cn("underline decoration-stone-400 underline-offset-4")}

Youtube

Embed Video

) } ` Hey thanks man @MODSetter

I have same problem with slash-comand can't seem to find a way to render BubbleMenu to call a Embed or Upload(Blob Api) Dialog like Notion. Will probably take a look at the code next week 😬

lumpinif commented 5 months ago

https://github.com/steven-tey/novel/assets/58391009/cd128cab-11c4-4faa-bba2-c08e000c50b4

I got both the selector and the slash command working, but I can't fix the position problem of the popover triggered by the slash command lol.

I can manually overwrite the position though but I just want the popover to be where we trigger the slash command of youtube

MODSetter commented 5 months ago

Recording.2024-04-27.211125.mp4 I got both the selector and the slash command working, but I can't fix the position problem of the popover triggered by the slash command lol.

I can manually overwrite the position though but I just want the popover to be where we trigger the slash command of youtube

What was the workaround around slash?

lumpinif commented 5 months ago

Recording.2024-04-27.211125.mp4 I got both the selector and the slash command working, but I can't fix the position problem of the popover triggered by the slash command lol. I can manually overwrite the position though but I just want the popover to be where we trigger the slash command of youtube

What was the workaround around slash?

basically, the main idea is to trigger the youtube selector by using slash since we have configured the youtube selector to work as expected, I couldn't use editor.chain(...).focus(...).deleteRange(...).setYoutubeVideo in the slash due to the ts errors say it is not a function, and I think we shouldn't directly use slash to setYoutubeVideo, because it seems too complicated to implement all the UI it needed in the slash such as toast or a popover (or modal, I used modal cuz I couldn't configure the popover to be right position of the slash)

MODSetter commented 5 months ago

Merge Conflicts with recent merges to main. Will raise PR after resolving them.

CodeLine6 commented 4 months ago

The Wordcounter does not work as expected when there are two or more active instances of editor.

MODSetter commented 4 months ago

The Wordcounter does not work as expected when there are two or more active instances of editor.

Can't understand.... main webapp(novel.sh) is using 2 instances one in dialog and it works fine.

CodeLine6 commented 4 months ago

The Wordcounter does not work as expected when there are two or more active instances of editor.

Can't understand.... main webapp(novel.sh) is using 2 instances one in dialog and it works fine.

You are right. I can't understand why its not the case on my app. After debugging I found out that using editor.storage.characterCount.words() for two different instances returns same value which is the actual word count of the most recent instance; Although when I log editor content for both instances using editor.getHTML(), it works completely fine.

Here is test deployment: https://deploy-preview-2--noteeasy.netlify.app/ Repo Link : https://github.com/missingsemicolononline8/noteeasy/tree/ea77f1611f56e23619548b17a6f80a90eaf03adf

The two instances are

1) Add New Note Editor image

2) Modify Note Editor: Editor which will appear when you edit any note image

Note : If you want to use the test app, you should signup using registration form as using login with google will redirect you to production url which we don't want.

MODSetter commented 4 months ago

The Wordcounter does not work as expected when there are two or more active instances of editor.

Can't understand.... main webapp(novel.sh) is using 2 instances one in dialog and it works fine.

You are right. I can't understand why its not the case on my app. After debugging I found out that using editor.storage.characterCount.words() for two different instances returns same value which is the actual word count of the most recent instance; Although when I log editor content for both instances using editor.getHTML(), it works completely fine.

Here is test deployment: https://deploy-preview-2--noteeasy.netlify.app/ Repo Link : https://github.com/missingsemicolononline8/noteeasy/tree/ea77f1611f56e23619548b17a6f80a90eaf03adf

The two instances are

1) Add New Note Editor image

2) Modify Note Editor: Editor which will appear when you edit any note image

Note : If you want to use the test app, you should signup using registration form as using login with google will redirect you to production url which we don't want.

Use this editor component: https://github.com/steven-tey/novel/tree/main/apps%2Fweb%2Fcomponents%2Ftailwind

It have latest update implementation

CodeLine6 commented 4 months ago

The Wordcounter does not work as expected when there are two or more active instances of editor.

Can't understand.... main webapp(novel.sh) is using 2 instances one in dialog and it works fine.

You are right. I can't understand why its not the case on my app. After debugging I found out that using editor.storage.characterCount.words() for two different instances returns same value which is the actual word count of the most recent instance; Although when I log editor content for both instances using editor.getHTML(), it works completely fine. Here is test deployment: https://deploy-preview-2--noteeasy.netlify.app/ Repo Link : https://github.com/missingsemicolononline8/noteeasy/tree/ea77f1611f56e23619548b17a6f80a90eaf03adf The two instances are

  1. Add New Note Editor image
  2. Modify Note Editor: Editor which will appear when you edit any note image

Note : If you want to use the test app, you should signup using registration form as using login with google will redirect you to production url which we don't want.

Use this editor component: https://github.com/steven-tey/novel/tree/main/apps%2Fweb%2Fcomponents%2Ftailwind

It have latest update implementation

Would using this solve the issue?