Compare commits

...

10 Commits

23 changed files with 1287 additions and 104 deletions

View File

@ -0,0 +1,239 @@
import type { JSX, SVGProps } from 'react'
export const BoldIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={20}
height={20}
viewBox="0 0 20 20"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M11.4062 15.625H5.625V4.375H10.9375C11.5639 4.37504 12.1771 4.55435 12.7048 4.89174C13.2325 5.22914 13.6526 5.71052 13.9155 6.27903C14.1784 6.84754 14.2731 7.47942 14.1884 8.10001C14.1037 8.72061 13.8431 9.30399 13.4375 9.78125C13.9673 10.205 14.3528 10.7825 14.5408 11.4344C14.7289 12.0862 14.7102 12.7803 14.4875 13.4211C14.2647 14.0619 13.8488 14.6179 13.297 15.0126C12.7452 15.4073 12.0847 15.6213 11.4062 15.625ZM7.5 13.75H11.3937C11.5784 13.75 11.7613 13.7136 11.9319 13.643C12.1025 13.5723 12.2575 13.4687 12.3881 13.3381C12.5187 13.2075 12.6223 13.0525 12.693 12.8819C12.7636 12.7113 12.8 12.5284 12.8 12.3438C12.8 12.1591 12.7636 11.9762 12.693 11.8056C12.6223 11.635 12.5187 11.48 12.3881 11.3494C12.2575 11.2188 12.1025 11.1152 11.9319 11.0445C11.7613 10.9739 11.5784 10.9375 11.3937 10.9375H7.5V13.75ZM7.5 9.0625H10.9375C11.1222 9.0625 11.305 9.02613 11.4756 8.95546C11.6463 8.88478 11.8013 8.7812 11.9319 8.65062C12.0625 8.52004 12.166 8.36501 12.2367 8.1944C12.3074 8.02378 12.3438 7.84092 12.3438 7.65625C12.3438 7.47158 12.3074 7.28872 12.2367 7.1181C12.166 6.94749 12.0625 6.79246 11.9319 6.66188C11.8013 6.5313 11.6463 6.42772 11.4756 6.35704C11.305 6.28637 11.1222 6.25 10.9375 6.25H7.5V9.0625Z"
fill="currentColor"
/>
</svg>
)
}
export const ItalicIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M19.625 9.625v-1.25H11.5v1.25h3.213l-2.732 8.75H8.375v1.25H16.5v-1.25h-3.213l2.732-8.75h3.606z"
fill="currentColor"
/>
</svg>
)
}
export const UnderlineIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M6.5 20.25h15v1.25h-15v-1.25zm7.5-1.875A4.375 4.375 0 019.625 14V7.125h1.25V14a3.125 3.125 0 106.25 0V7.125h1.25V14A4.375 4.375 0 0114 18.375z"
fill="#212529"
/>
</svg>
)
}
export const UndoIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M16.5 10.25H8.884l2.242-2.241-.876-.884-3.75 3.75 3.75 3.75.876-.884-2.24-2.241H16.5a3.75 3.75 0 010 7.5h-5v1.25h5a5 5 0 100-10z"
fill="#212529"
/>
</svg>
)
}
export const RedoIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M11.5 10.25h7.616l-2.242-2.241.876-.884 3.75 3.75-3.75 3.75-.876-.884 2.24-2.241H11.5a3.75 3.75 0 000 7.5h5v1.25h-5a5 5 0 010-10z"
fill="#212529"
/>
</svg>
)
}
export const ListIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M8.375 11.5a1.875 1.875 0 100-3.75 1.875 1.875 0 000 3.75zM8.375 20.25a1.875 1.875 0 100-3.75 1.875 1.875 0 000 3.75zM14 17.75h8.75V19H14v-1.25zM14 9h8.75v1.25H14V9z"
fill="#212529"
/>
</svg>
)
}
export const ListOrderIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M14 17.75h8.75V19H14v-1.25zM14 9h8.75v1.25H14V9zm-5 2.5v-5H7.75v.625H6.5v1.25h1.25V11.5H6.5v1.25h3.75V11.5H9zm1.25 10H6.5V19a1.25 1.25 0 011.25-1.25H9V16.5H6.5v-1.25H9a1.25 1.25 0 011.25 1.25v1.25A1.25 1.25 0 019 19H7.75v1.25h2.5v1.25z"
fill="#212529"
/>
</svg>
)
}
export const LinkIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M22.281 8.225a3.75 3.75 0 00-5.312 0l.887.888a2.506 2.506 0 113.544 3.543l-5 5a2.504 2.504 0 01-3.544-3.537l.882-.888-.882-.887-.887.887a3.75 3.75 0 000 5.313 3.75 3.75 0 002.656 1.081 3.75 3.75 0 002.669-1.1l5-5a3.75 3.75 0 00-.013-5.3z"
fill="#212529"
/>
<path
d="M6.619 19.512a2.5 2.5 0 010-3.543l5-5a2.5 2.5 0 013.543 0 2.462 2.462 0 01.713 1.781 2.5 2.5 0 01-.731 1.781l-1.325 1.344.887.887 1.325-1.325a3.76 3.76 0 10-5.318-5.318l-5 5a3.75 3.75 0 000 5.319A3.75 3.75 0 008.375 21.5a3.794 3.794 0 002.675-1.1l-.888-.887a2.5 2.5 0 01-3.543 0z"
fill="#212529"
/>
</svg>
)
}
export const ImageIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M15.875 12.75a1.875 1.875 0 100-3.75 1.875 1.875 0 000 3.75zm0-2.5a.625.625 0 110 1.25.625.625 0 010-1.25z"
fill="#212529"
/>
<path
d="M20.25 6.5H7.75A1.25 1.25 0 006.5 7.75v12.5a1.25 1.25 0 001.25 1.25h12.5a1.25 1.25 0 001.25-1.25V7.75a1.25 1.25 0 00-1.25-1.25zm0 13.75H7.75V16.5l3.125-3.125 3.494 3.494a1.25 1.25 0 001.762 0l.994-.994L20.25 19v1.25zm0-3.019l-2.244-2.244a1.25 1.25 0 00-1.762 0l-.994.994-3.494-3.493a1.25 1.25 0 00-1.762 0L7.75 14.73V7.75h12.5v9.481z"
fill="#212529"
/>
</svg>
)
}
export const CodeIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M23.375 14L19 18.375l-.881-.881L21.606 14l-3.487-3.494.881-.881L23.375 14zm-18.75 0L9 9.625l.881.881L6.394 14l3.487 3.494-.881.881L4.625 14zm7.137 5.927L15.025 7.75l1.208.323L12.97 20.25l-1.208-.323z"
fill="#212529"
/>
</svg>
)
}
export const QuoteIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M11.5 13.375H7.819a5.626 5.626 0 012.431-3.838l1.119-.75-.688-1.037-1.118.75A6.875 6.875 0 006.5 14.219v4.156a1.25 1.25 0 001.25 1.25h3.75a1.25 1.25 0 001.25-1.25v-3.75a1.25 1.25 0 00-1.25-1.25zm8.75 0h-3.681A5.625 5.625 0 0119 9.537l1.119-.75-.681-1.037-1.125.75a6.875 6.875 0 00-3.063 5.719v4.156a1.25 1.25 0 001.25 1.25h3.75a1.25 1.25 0 001.25-1.25v-3.75a1.25 1.25 0 00-1.25-1.25z"
fill="#212529"
/>
</svg>
)
}
export const StrikethroughIcon = (
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) => {
return (
<svg
width={28}
height={28}
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...properties}
>
<path
d="M21.5 13.375h-6.277a25.033 25.033 0 00-.837-.209c-1.755-.415-2.747-.719-2.747-2.14a1.792 1.792 0 01.492-1.34 2.993 2.993 0 011.883-.681c1.769-.044 2.584.556 3.251 1.468l1.01-.737a4.67 4.67 0 00-4.27-1.981 4.233 4.233 0 00-2.75 1.038 3.015 3.015 0 00-.866 2.234 2.733 2.733 0 001.084 2.348H6.5v1.25h8.533c1.229.357 1.964.82 1.983 2.1a1.949 1.949 0 01-.54 1.495 3.64 3.64 0 01-2.324.78 4.146 4.146 0 01-3.216-1.681l-.958.802a5.328 5.328 0 004.155 2.13h.062a4.794 4.794 0 003.155-1.138 3.175 3.175 0 00.916-2.408 3.094 3.094 0 00-.72-2.08H21.5v-1.25z"
fill="#212529"
/>
</svg>
)
}

View File

@ -6,7 +6,7 @@ import {
DialogTitle,
} from '@headlessui/react'
import type { ReactNode } from 'react'
import { useRouteLoaderData } from 'react-router'
import { Link, useRouteLoaderData } from 'react-router'
import { LeftArrow } from '~/components/icons/left-arrow'
import { Button } from '~/components/ui/button'
@ -34,9 +34,9 @@ const DESCRIPTIONS: DescriptionMap = {
}
export const SuccessModal = ({ isOpen, onClose }: ModalProperties) => {
const { setIsLoginOpen, setIsInitSubscribeOpen } = useNewsContext()
const { setIsLoginOpen, setIsSubscribeOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>('routes/_layout')
const userToken = loaderData?.userToken
const userData = loaderData?.userData
const message = isOpen
? DESCRIPTIONS[isOpen]
@ -93,6 +93,8 @@ export const SuccessModal = ({ isOpen, onClose }: ModalProperties) => {
<Button
className="mt-5 w-full rounded-md"
variant="newsPrimary"
as={Link}
to="/"
onClick={onClose}
>
Back to Home
@ -106,13 +108,13 @@ export const SuccessModal = ({ isOpen, onClose }: ModalProperties) => {
alt={APP.title}
className="h-[300px]"
/>
{userToken ? (
{userData ? (
<Button
className="mt-5 w-full rounded-md"
variant="newsSecondary"
onClick={() => {
onClose()
setIsInitSubscribeOpen(true)
setIsSubscribeOpen(true)
}}
>
Select Subscription

View File

@ -0,0 +1,24 @@
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
type TCarouselButton = {
direction: 'prev' | 'next'
isEnabled: boolean
onClick: () => void
}
export const CarouselButton = ({
direction,
isEnabled,
onClick,
}: TCarouselButton) => {
const Icon = direction === 'prev' ? CarouselPreviousIcon : CarouselNextIcon
return (
<Icon
color={isEnabled ? '#2E2F7C' : '#DCDCDC'}
className={isEnabled ? 'cursor-pointer' : 'cursor-not-allowed'}
width={45}
height={45}
onClick={onClick}
/>
)
}

View File

@ -1,25 +1,41 @@
import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react'
import { Link } from 'react-router'
import { useCallback, useEffect, useState } from 'react'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button'
import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render'
export const CarouselHero = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: true })
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: false })
const [canScrollNext, setCanScrollNext] = useState(false)
const [canScrollPrevious, setCanScrollPrevious] = useState(false)
const updateButtons = useCallback(() => {
if (emblaApi) {
setCanScrollPrevious(emblaApi.canScrollPrev())
setCanScrollNext(emblaApi.canScrollNext())
}
}, [emblaApi])
useEffect(() => {
if (emblaApi) {
updateButtons()
emblaApi.on('select', updateButtons)
}
}, [emblaApi, updateButtons])
const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
if (canScrollPrevious && emblaApi) emblaApi.scrollPrev()
}, [emblaApi, canScrollPrevious])
const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
if (canScrollNext && emblaApi) emblaApi.scrollNext()
}, [emblaApi, canScrollNext])
return (
<div className="">
@ -33,18 +49,14 @@ export const CarouselHero = (properties: TNews) => {
</p>
</div>
<div className="flex gap-2.5">
<CarouselPreviousIcon
color="#DCDCDC"
className="cursor-pointer"
width={45}
height={45}
<CarouselButton
direction="prev"
isEnabled={canScrollPrevious}
onClick={previousSlide}
/>
<CarouselNextIcon
color="#2E2F7C"
className="cursor-pointer"
width={45}
height={45}
<CarouselButton
direction="next"
isEnabled={canScrollNext}
onClick={nextSlide}
/>
</div>
@ -77,14 +89,11 @@ export const CarouselHero = (properties: TNews) => {
</div>
<Button
size="block"
{...(isPremium
? {
onClick: () => {
setIsSuccessOpen('warning')
},
to: '',
}
: { as: Link, to: `/detail/${slug}` })}
{...getPremiumAttribute({
isPremium,
slug,
onClick: () => setIsSuccessOpen('warning'),
})}
>
View More
</Button>

View File

@ -1,25 +1,46 @@
import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react'
import { Link } from 'react-router'
import { useCallback, useEffect, useState } from 'react'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button'
import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render'
export const CarouselSection = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: false })
const [emblaReference, emblaApi] = useEmblaCarousel({
loop: false,
slidesToScroll: 1,
align: 'start',
})
const [canScrollNext, setCanScrollNext] = useState(false)
const [canScrollPrevious, setCanScrollPrevious] = useState(false)
const updateButtons = useCallback(() => {
if (emblaApi) {
setCanScrollPrevious(emblaApi.canScrollPrev())
setCanScrollNext(emblaApi.canScrollNext())
}
}, [emblaApi])
useEffect(() => {
if (emblaApi) {
updateButtons()
emblaApi.on('select', updateButtons)
}
}, [emblaApi, updateButtons])
const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
if (canScrollPrevious && emblaApi) emblaApi.scrollPrev()
}, [emblaApi, canScrollPrevious])
const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
if (canScrollNext && emblaApi) emblaApi.scrollNext()
}, [emblaApi, canScrollNext])
return (
<div className="">
<div className="mt-3 mb-3 flex items-center justify-between border-b border-black pb-3 sm:mb-[30px] sm:pb-[30px]">
@ -33,18 +54,14 @@ export const CarouselSection = (properties: TNews) => {
</div>
<div className="flex gap-2.5">
<CarouselPreviousIcon
color="#DCDCDC"
className="cursor-pointer"
width={45}
height={45}
<CarouselButton
direction="prev"
isEnabled={canScrollPrevious}
onClick={previousSlide}
/>
<CarouselNextIcon
color="#2E2F7C"
className="cursor-pointer"
width={45}
height={45}
<CarouselButton
direction="next"
isEnabled={canScrollNext}
onClick={nextSlide}
/>
</div>
@ -94,14 +111,11 @@ export const CarouselSection = (properties: TNews) => {
</div>
<Button
size="block"
{...(isPremium
? {
onClick: () => {
setIsSuccessOpen('warning')
},
to: '',
}
: { as: Link, to: `/detail/${slug}` })}
{...getPremiumAttribute({
isPremium,
slug,
onClick: () => setIsSuccessOpen('warning'),
})}
className="mb-5"
>
View More

View File

@ -1,4 +1,3 @@
import { Link } from 'react-router'
import { twMerge } from 'tailwind-merge'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
@ -6,6 +5,7 @@ import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button'
import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news'
import { getPremiumAttribute } from '~/utils/render'
export const CategorySection = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext()
@ -82,14 +82,11 @@ export const CategorySection = (properties: TNews) => {
</div>
<Button
size="block"
{...(isPremium
? {
onClick: () => {
setIsSuccessOpen('warning')
},
to: '',
}
: { as: Link, to: `/detail/${slug}` })}
{...getPremiumAttribute({
isPremium,
slug,
onClick: () => setIsSuccessOpen('warning'),
})}
className="mb-5"
>
View More

View File

@ -0,0 +1,106 @@
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import {
BoldIcon as Bold,
ItalicIcon as Italic,
UndoIcon as Undo,
RedoIcon as Redo,
ListIcon as List,
ListOrderIcon as ListOrdered,
LinkIcon as Link,
ImageIcon as Image,
CodeIcon as Code,
QuoteIcon as Quote,
StrikethroughIcon as Strikethrough,
UnderlineIcon as Underline,
} from '~/components/icons/editor'
const DefaultTextEditor = () => {
const editor = useEditor({
extensions: [StarterKit],
content:
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quis lobortis nisl cursus bibendum sit nulla accumsan sodales ornare. At urna viverra non suspendisse neque, lorem. Pretium condimentum pellentesque gravida id etiam sit sed arcu euismod. Rhoncus proin orci duis scelerisque molestie cursus tincidunt aliquam.</p>',
})
if (!editor) {
return <p>Loading editor...</p>
}
return (
<div className="0 rounded border bg-white">
<div className="mb-4 rounded bg-gray-100 p-2">
<div className="flex items-center gap-2">
<button onClick={() => editor.chain().focus().undo().run()}>
<Undo />
</button>
<button onClick={() => editor.chain().focus().redo().run()}>
<Redo />
</button>
<button onClick={() => editor.chain().focus().toggleBold().run()}>
<Bold />
</button>
<button onClick={() => editor.chain().focus().toggleItalic().run()}>
<Italic />
</button>
<button
// onClick={() => editor.chain().focus().toggleUnderline().run()}
>
<Underline />
</button>
<button onClick={() => editor.chain().focus().toggleStrike().run()}>
<Strikethrough />
</button>
<button
onClick={() => editor.chain().focus().toggleBulletList().run()}
>
<List />
</button>
<button
onClick={() => editor.chain().focus().toggleOrderedList().run()}
>
<ListOrdered />
</button>
<button onClick={() => editor.chain().focus().toggleCode().run()}>
<Code />
</button>
<button
onClick={() => editor.chain().focus().toggleBlockquote().run()}
>
<Quote />
</button>
<button
onClick={() =>
editor
.chain()
.focus()
// .setLink({ href: prompt('Enter URL') })
.run()
}
>
<Link />
</button>
<button
onClick={() =>
editor
.chain()
.focus()
// .setImage({ src: prompt('Enter image URL') })
.run()
}
>
<Image />
</button>
</div>
</div>
<div className="p-5">
<EditorContent
editor={editor}
className="h-[50vh]"
/>
</div>
</div>
)
}
export default DefaultTextEditor

View File

@ -20,8 +20,8 @@ type NewsContextProperties = {
setIsSuccessOpen: Dispatch<
SetStateAction<ModalProperties['isOpen'] | undefined>
>
isInitSubscribeOpen: boolean
setIsInitSubscribeOpen: Dispatch<SetStateAction<boolean>>
isSubscribeOpen: boolean
setIsSubscribeOpen: Dispatch<SetStateAction<boolean>>
}
const NewsContext = createContext<NewsContextProperties | undefined>(undefined)
@ -32,7 +32,7 @@ export const NewsProvider = ({ children }: PropsWithChildren) => {
const [isForgetOpen, setIsForgetOpen] = useState(false)
const [isSuccessOpen, setIsSuccessOpen] =
useState<ModalProperties['isOpen']>()
const [isInitSubscribeOpen, setIsInitSubscribeOpen] = useState(false)
const [isSubscribeOpen, setIsSubscribeOpen] = useState(false)
return (
<NewsContext.Provider
@ -45,8 +45,8 @@ export const NewsProvider = ({ children }: PropsWithChildren) => {
setIsForgetOpen,
isSuccessOpen,
setIsSuccessOpen,
isInitSubscribeOpen,
setIsInitSubscribeOpen,
isSubscribeOpen,
setIsSubscribeOpen,
}}
>
{children}

View File

@ -25,8 +25,8 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
setIsForgetOpen,
isSuccessOpen,
setIsSuccessOpen,
isInitSubscribeOpen,
setIsInitSubscribeOpen,
isSubscribeOpen,
setIsSubscribeOpen,
} = useNewsContext()
return (
<main className="relative min-h-dvh bg-[#ECECEC]">
@ -69,8 +69,8 @@ export const NewsDefaultLayout = (properties: PropsWithChildren) => {
</PopupModal>
<PopupModal
isOpen={isInitSubscribeOpen}
onClose={() => setIsInitSubscribeOpen(false)}
isOpen={isSubscribeOpen}
onClose={() => setIsSubscribeOpen(false)}
description="Selamat Datang, silakan Pilih Subscription Anda untuk melanjutkan!"
>
<FormSubscription />

View File

@ -20,7 +20,7 @@ export const FormLogin = () => {
setIsRegisterOpen,
setIsLoginOpen,
setIsForgetOpen,
setIsInitSubscribeOpen,
setIsSubscribeOpen,
} = useNewsContext()
const fetcher = useFetcher()
const [error, setError] = useState<string>()
@ -46,7 +46,7 @@ export const FormLogin = () => {
setIsLoginOpen(false)
if (fetcher.data?.user.subscribe_plan_code === 'basic') {
setIsInitSubscribeOpen(true)
setIsSubscribeOpen(true)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher])

View File

@ -26,7 +26,8 @@ export const registerSchema = z
export type TRegisterSchema = z.infer<typeof registerSchema>
export const FormRegister = () => {
const { setIsLoginOpen, setIsRegisterOpen } = useNewsContext()
const { setIsLoginOpen, setIsRegisterOpen, setIsSuccessOpen } =
useNewsContext()
const [error, setError] = useState<string>()
const [disabled, setDisabled] = useState(false)
const fetcher = useFetcher()
@ -51,6 +52,7 @@ export const FormRegister = () => {
setDisabled(true)
setError(undefined)
setIsRegisterOpen(false)
setIsSuccessOpen('register')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher])

View File

@ -1,29 +1,80 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { useEffect, useState } from 'react'
import { useFetcher, useRouteLoaderData } from 'react-router'
import { RemixFormProvider, useRemixForm } from 'remix-hook-form'
import { z } from 'zod'
import { Button } from '~/components/ui/button'
import { Select } from '~/components/ui/select'
import { useNewsContext } from '~/contexts/news'
import type { loader } from '~/routes/_layout'
export const subscribeSchema = z.object({
subscribe_plan: z.string().min(1, 'Pilih salah satu subscription'),
})
export type TSubscribeSchema = z.infer<typeof subscribeSchema>
export default function FormSubscription() {
const { setIsSubscribeOpen, setIsSuccessOpen } = useNewsContext()
const fetcher = useFetcher()
const [error, setError] = useState<string>()
const [disabled, setDisabled] = useState(false)
const loaderData = useRouteLoaderData<typeof loader>('routes/_layout')
const subscriptions = loaderData?.subscriptionsData
const formMethods = useRemixForm<TSubscribeSchema>({
mode: 'onSubmit',
fetcher,
resolver: zodResolver(subscribeSchema),
})
const { handleSubmit } = formMethods
useEffect(() => {
if (!fetcher.data?.success) {
setError(fetcher.data?.message)
setDisabled(false)
return
}
setDisabled(true)
setError(undefined)
setIsSubscribeOpen(false)
setIsSuccessOpen('payment')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher])
return (
<div className="flex flex-col items-center justify-center">
<div className="w-full max-w-md">
<form>
{/* Subscribe*/}
<div className="mb-4">
<label
htmlFor="subscription"
className="mb-1 block text-gray-700"
<RemixFormProvider {...formMethods}>
<fetcher.Form
method="post"
onSubmit={handleSubmit}
className="w-full max-w-md"
action="/actions/subscribe"
>
Subscription
</label>
<select className="focus:inheriten w-full rounded-md border border-[#DFDFDF] p-2">
<option selected>Subscription</option>
</select>
</div>
<Select
id="subscribe_plan"
name="subscribe_plan"
label="Subscription"
placeholder="Pilih Subscription"
options={subscriptions}
/>
{/* Tombol Masuk */}
<Button className="mt-5 w-full rounded-md bg-[#2E2F7C] py-2 text-white transition hover:bg-blue-800">
{error && (
<div className="text-sm text-red-500 capitalize">{error}</div>
)}
<Button
disabled={disabled}
type="submit"
className="mt-5 w-full rounded-md bg-[#2E2F7C] py-2 text-white transition hover:bg-blue-800"
>
Lanjutkan
</Button>
</form>
</div>
</fetcher.Form>
</RemixFormProvider>
</div>
)
}

View File

@ -8,7 +8,7 @@ import type { loader } from '~/routes/_layout'
export const HeaderTop = () => {
const { setIsLoginOpen } = useNewsContext()
const loaderData = useRouteLoaderData<typeof loader>('routes/_layout')
const userToken = loaderData?.userToken
const userData = loaderData?.userData
const fetcher = useFetcher()
return (
@ -30,7 +30,7 @@ export const HeaderTop = () => {
<Button className="h-8 w-auto rounded-none px-3 text-xs sm:h-[50px] sm:w-[150px] sm:text-lg">
About Us
</Button>
{userToken ? (
{userData ? (
<fetcher.Form
method="POST"
action="/actions/logout"

View File

@ -0,0 +1,45 @@
import { Field, Input, Label, Select } from '@headlessui/react'
import { SearchIcon } from '~/components/icons/search'
import DefaultTextEditor from '~/components/ui/text-editor'
import { TitleDashboard } from '~/components/ui/title-dashboard'
export const CreateContentsPage = () => {
return (
<div className="relative">
<TitleDashboard title="Konten" />
<div className="mb-8 flex items-center gap-5 rounded-lg bg-gray-50 text-[#363636]">
<div className="w-[400px]">
<Field>
<Label className="mb-2 block text-sm font-medium">Pilih Tags</Label>
<div className="relative">
<Input
type="text"
placeholder="Cari Tags"
className="w-full rounded-lg bg-white p-2 pr-10 pl-4 shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
<SearchIcon className="h-5 w-5" />
</div>
</div>
</Field>
</div>
<div className="w-[235px]">
<Field>
<Label className="mb-2 block text-sm font-medium">Status</Label>
<Select className="w-full rounded-lg bg-white p-2 shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none">
<option>Pilih Status</option>
<option>Aktif</option>
<option>Nonaktif</option>
</Select>
</Field>
</div>
</div>
<section>
<DefaultTextEditor />
</section>
</div>
)
}

View File

@ -0,0 +1,3 @@
export const UpdateContentsPage = () => {
return <div>detail</div>
}

View File

@ -3,6 +3,7 @@ import DT from 'datatables.net-dt'
import DataTable from 'datatables.net-react'
import { SearchIcon } from '~/components/icons/search'
import { Button } from '~/components/ui/button'
import { TitleDashboard } from '~/components/ui/title-dashboard'
import { CONTENTS } from './data'
@ -27,9 +28,6 @@ export const ContentsPage = () => {
{
title: 'Action',
data: 'id',
render: () => {
return '<button class="bg-[#2E2F7C] text-white px-2 py-1 rounded-md">Lihat Detail</button>'
},
},
]
@ -70,6 +68,18 @@ export const ContentsPage = () => {
className="cell-border"
data={CONTENTS}
columns={columns}
slots={{
6: (value: string | number) => {
return (
<Button
as="a"
href={`${value}`}
>
Lihat Detail
</Button>
)
},
}}
options={{
paging: true,
searching: true,

View File

@ -0,0 +1,11 @@
import { AdminDashboardLayout } from '~/layouts/admin/dashboard'
import { CreateContentsPage } from '~/pages/contents-create'
const DashboardContentsLayout = () => {
return (
<AdminDashboardLayout>
<CreateContentsPage />
</AdminDashboardLayout>
)
}
export default DashboardContentsLayout

View File

@ -0,0 +1,11 @@
import { AdminDashboardLayout } from '~/layouts/admin/dashboard'
import { UpdateContentsPage } from '~/pages/contents-update'
const DashboardContentsLayout = () => {
return (
<AdminDashboardLayout>
<UpdateContentsPage />
</AdminDashboardLayout>
)
}
export default DashboardContentsLayout

View File

@ -2,6 +2,7 @@ import { Outlet } from 'react-router'
import { getCategories } from '~/apis/common/get-categories'
import { getSubscriptions } from '~/apis/common/get-subscriptions'
import { getUser } from '~/apis/news/get-user'
import { NewsProvider } from '~/contexts/news'
import { NewsDefaultLayout } from '~/layouts/news/default'
import { handleCookie } from '~/libs/cookies'
@ -10,11 +11,18 @@ import type { Route } from './+types/_layout'
export const loader = async ({ request }: Route.LoaderArgs) => {
const { userToken } = await handleCookie(request)
let userData
if (userToken) {
const { data } = await getUser({
accessToken: userToken,
})
userData = data
}
const { data: subscriptionsData } = await getSubscriptions()
const { data: categoriesData } = await getCategories()
return {
userToken,
userData,
subscriptionsData,
categoriesData,
}

View File

@ -0,0 +1,69 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { data } from 'react-router'
import { getValidatedFormData } from 'remix-hook-form'
import { XiorError } from 'xior'
import { getUser } from '~/apis/news/get-user'
import {
subscribeSchema,
type TSubscribeSchema,
} from '~/layouts/news/form-subscription'
import { handleCookie } from '~/libs/cookies'
import type { Route } from './+types/actions.register'
export const action = async ({ request }: Route.ActionArgs) => {
const { userToken } = await handleCookie(request)
try {
const {
errors,
data: payload,
receivedValues: defaultValues,
} = await getValidatedFormData<TSubscribeSchema>(
request,
zodResolver(subscribeSchema),
false,
)
if (errors) {
return data({ success: false, errors, defaultValues }, { status: 400 })
}
// TODO: implement subscribe
console.log('payload', payload) // eslint-disable-line no-console
const { data: userData } = await getUser({
accessToken: userToken,
})
return data(
{
success: true,
user: userData,
},
{
status: 200,
statusText: 'OK',
},
)
} catch (error) {
if (error instanceof XiorError) {
return data(
{
success: false,
message: error?.response?.data?.error?.message || error.message,
},
{
status: error?.response?.status || 500,
},
)
}
return data(
{
success: false,
message: 'Internal server error',
},
{ status: 500 },
)
}
}

23
app/utils/render.ts Normal file
View File

@ -0,0 +1,23 @@
import type { MouseEventHandler } from 'react'
import { Link } from 'react-router'
type TGetPremiumAttribute = {
isPremium?: boolean
slug: string
onClick: MouseEventHandler<HTMLElement>
}
export const getPremiumAttribute = (parameters: TGetPremiumAttribute) => {
const { isPremium, slug, onClick } = parameters
if (isPremium) {
return {
onClick,
to: '',
}
}
return {
as: Link,
to: `/detail/${slug}`,
}
}

View File

@ -19,6 +19,8 @@
"@react-router/fs-routes": "^7.1.3",
"@react-router/node": "^7.1.3",
"@react-router/serve": "^7.1.3",
"@tiptap/react": "^2.11.5",
"@tiptap/starter-kit": "^2.11.5",
"chart.js": "^4.4.8",
"class-variance-authority": "^0.7.1",
"datatables.net-dt": "^2.2.2",

557
pnpm-lock.yaml generated
View File

@ -23,6 +23,12 @@ importers:
'@react-router/serve':
specifier: ^7.1.3
version: 7.1.3(react-router@7.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3)
'@tiptap/react':
specifier: ^2.11.5
version: 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@tiptap/starter-kit':
specifier: ^2.11.5
version: 2.11.5
chart.js:
specifier: ^4.4.8
version: 4.4.8
@ -705,6 +711,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
'@radix-ui/number@1.0.1':
resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==}
@ -1178,6 +1187,9 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@remirror/core-constants@3.0.0':
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
'@rollup/rollup-android-arm-eabi@4.32.1':
resolution: {integrity: sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==}
cpu: [arm]
@ -1381,6 +1393,137 @@ packages:
'@tanstack/virtual-core@3.13.0':
resolution: {integrity: sha512-NBKJP3OIdmZY3COJdWkSonr50FMVIi+aj5ZJ7hI/DTpEKg2RMfo/KvP8A3B/zOSpMgIe52B5E2yn7rryULzA6g==}
'@tiptap/core@2.11.5':
resolution: {integrity: sha512-jb0KTdUJaJY53JaN7ooY3XAxHQNoMYti/H6ANo707PsLXVeEqJ9o8+eBup1JU5CuwzrgnDc2dECt2WIGX9f8Jw==}
peerDependencies:
'@tiptap/pm': ^2.7.0
'@tiptap/extension-blockquote@2.11.5':
resolution: {integrity: sha512-MZfcRIzKRD8/J1hkt/eYv49060GTL6qGR3NY/oTDuw2wYzbQXXLEbjk8hxAtjwNn7G+pWQv3L+PKFzZDxibLuA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-bold@2.11.5':
resolution: {integrity: sha512-OAq03MHEbl7MtYCUzGuwb0VpOPnM0k5ekMbEaRILFU5ZC7cEAQ36XmPIw1dQayrcuE8GZL35BKub2qtRxyC9iA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-bubble-menu@2.11.5':
resolution: {integrity: sha512-rx+rMd7EEdht5EHLWldpkzJ56SWYA9799b33ustePqhXd6linnokJCzBqY13AfZ9+xp3RsR6C0ZHI9GGea0tIA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-bullet-list@2.11.5':
resolution: {integrity: sha512-VXwHlX6A/T6FAspnyjbKDO0TQ+oetXuat6RY1/JxbXphH42nLuBaGWJ6pgy6xMl6XY8/9oPkTNrfJw/8/eeRwA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-code-block@2.11.5':
resolution: {integrity: sha512-ksxMMvqLDlC+ftcQLynqZMdlJT1iHYZorXsXw/n+wuRd7YElkRkd6YWUX/Pq/njFY6lDjKiqFLEXBJB8nrzzBA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-code@2.11.5':
resolution: {integrity: sha512-xOvHevNIQIcCCVn9tpvXa1wBp0wHN/2umbAZGTVzS+AQtM7BTo0tz8IyzwxkcZJaImONcUVYLOLzt2AgW1LltA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-document@2.11.5':
resolution: {integrity: sha512-7I4BRTpIux2a0O2qS3BDmyZ5LGp3pszKbix32CmeVh7lN9dV7W5reDqtJJ9FCZEEF+pZ6e1/DQA362dflwZw2g==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-dropcursor@2.11.5':
resolution: {integrity: sha512-uIN7L3FU0904ec7FFFbndO7RQE/yiON4VzAMhNn587LFMyWO8US139HXIL4O8dpZeYwYL3d1FnDTflZl6CwLlg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-floating-menu@2.11.5':
resolution: {integrity: sha512-HsMI0hV5Lwzm530Z5tBeyNCBNG38eJ3qjfdV2OHlfSf3+KOEfn6a5AUdoNaZO02LF79/8+7BaYU2drafag9cxQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-gapcursor@2.11.5':
resolution: {integrity: sha512-kcWa+Xq9cb6lBdiICvLReuDtz/rLjFKHWpW3jTTF3FiP3wx4H8Rs6bzVtty7uOVTfwupxZRiKICAMEU6iT0xrQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-hard-break@2.11.5':
resolution: {integrity: sha512-q9doeN+Yg9F5QNTG8pZGYfNye3tmntOwch683v0CCVCI4ldKaLZ0jG3NbBTq+mosHYdgOH2rNbIORlRRsQ+iYQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-heading@2.11.5':
resolution: {integrity: sha512-x/MV53psJ9baRcZ4k4WjnCUBMt8zCX7mPlKVT+9C/o+DEs/j/qxPLs95nHeQv70chZpSwCQCt93xMmuF0kPoAg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-history@2.11.5':
resolution: {integrity: sha512-b+wOS33Dz1azw6F1i9LFTEIJ/gUui0Jwz5ZvmVDpL2ZHBhq1Ui0/spTT+tuZOXq7Y/uCbKL8Liu4WoedIvhboQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-horizontal-rule@2.11.5':
resolution: {integrity: sha512-3up2r1Du8/5/4ZYzTC0DjTwhgPI3dn8jhOCLu73m5F3OGvK/9whcXoeWoX103hYMnGDxBlfOje71yQuN35FL4A==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
'@tiptap/extension-italic@2.11.5':
resolution: {integrity: sha512-9VGfb2/LfPhQ6TjzDwuYLRvw0A6VGbaIp3F+5Mql8XVdTBHb2+rhELbyhNGiGVR78CaB/EiKb6dO9xu/tBWSYA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-list-item@2.11.5':
resolution: {integrity: sha512-Mp5RD/pbkfW1vdc6xMVxXYcta73FOwLmblQlFNn/l/E5/X1DUSA4iGhgDDH4EWO3swbs03x2f7Zka/Xoj3+WLg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-ordered-list@2.11.5':
resolution: {integrity: sha512-Cu8KwruBNWAaEfshRQR0yOSaUKAeEwxW7UgbvF9cN/zZuKgK5uZosPCPTehIFCcRe+TBpRtZQh+06f/gNYpYYg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-paragraph@2.11.5':
resolution: {integrity: sha512-YFBWeg7xu/sBnsDIF/+nh9Arf7R0h07VZMd0id5Ydd2Qe3c1uIZwXxeINVtH0SZozuPIQFAT8ICe9M0RxmE+TA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-strike@2.11.5':
resolution: {integrity: sha512-PVfUiCqrjvsLpbIoVlegSY8RlkR64F1Rr2RYmiybQfGbg+AkSZXDeO0eIrc03//4gua7D9DfIozHmAKv1KN3ow==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-text-style@2.11.5':
resolution: {integrity: sha512-YUmYl0gILSd/u/ZkOmNxjNXVw+mu8fpC2f8G4I4tLODm0zCx09j9DDEJXSrM5XX72nxJQqtSQsCpNKnL0hfeEQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-text@2.11.5':
resolution: {integrity: sha512-Gq1WwyhFpCbEDrLPIHt5A8aLSlf8bfz4jm417c8F/JyU0J5dtYdmx0RAxjnLw1i7ZHE7LRyqqAoS0sl7JHDNSQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm@2.11.5':
resolution: {integrity: sha512-z9JFtqc5ZOsdQLd9vRnXfTCQ8v5ADAfRt9Nm7SqP6FUHII8E1hs38ACzf5xursmth/VonJYb5+73Pqxk1hGIPw==}
'@tiptap/react@2.11.5':
resolution: {integrity: sha512-Dp8eHL1G+R/C4+QzAczyb3t1ovexEIZx9ln7SGEM+cT1KHKAw9XGPRgsp92+NQaYI+EdEb/YqoBOSzQcd18/OQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
'@tiptap/starter-kit@2.11.5':
resolution: {integrity: sha512-SLI7Aj2ruU1t//6Mk8f+fqW+18uTqpdfLUJYgwu0CkqBckrkRZYZh6GVLk/02k3H2ki7QkFxiFbZrdbZdng0JA==}
'@types/conventional-commits-parser@5.0.1':
resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==}
@ -1396,6 +1539,15 @@ packages:
'@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
'@types/node@20.17.16':
resolution: {integrity: sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==}
@ -1413,6 +1565,9 @@ packages:
'@types/react@19.0.8':
resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==}
'@types/use-sync-external-store@0.0.6':
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
'@typescript-eslint/eslint-plugin@8.22.0':
resolution: {integrity: sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -1832,6 +1987,9 @@ packages:
typescript:
optional: true
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@ -2962,6 +3120,9 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
lint-staged@15.4.3:
resolution: {integrity: sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==}
engines: {node: '>=18.12.0'}
@ -3034,10 +3195,17 @@ packages:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@ -3240,6 +3408,9 @@ packages:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
own-keys@1.0.1:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
@ -3465,6 +3636,64 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
prosemirror-changeset@2.2.1:
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
prosemirror-collab@1.3.1:
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
prosemirror-commands@1.7.0:
resolution: {integrity: sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==}
prosemirror-dropcursor@1.8.1:
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
prosemirror-gapcursor@1.3.2:
resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==}
prosemirror-history@1.4.1:
resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==}
prosemirror-inputrules@1.4.0:
resolution: {integrity: sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==}
prosemirror-keymap@1.2.2:
resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==}
prosemirror-markdown@1.13.1:
resolution: {integrity: sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==}
prosemirror-menu@1.2.4:
resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==}
prosemirror-model@1.24.1:
resolution: {integrity: sha512-YM053N+vTThzlWJ/AtPtF1j0ebO36nvbmDy4U7qA2XQB8JVaQp1FmB9Jhrps8s+z+uxhhVTny4m20ptUvhk0Mg==}
prosemirror-schema-basic@1.2.3:
resolution: {integrity: sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==}
prosemirror-schema-list@1.5.0:
resolution: {integrity: sha512-gg1tAfH1sqpECdhIHOA/aLg2VH3ROKBWQ4m8Qp9mBKrOxQRW61zc+gMCI8nh22gnBzd1t2u1/NPLmO3nAa3ssg==}
prosemirror-state@1.4.3:
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
prosemirror-tables@1.6.4:
resolution: {integrity: sha512-TkDY3Gw52gRFRfRn2f4wJv5WOgAOXLJA2CQJYIJ5+kdFbfj3acR4JUW6LX2e1hiEBiUwvEhzH5a3cZ5YSztpIA==}
prosemirror-trailing-node@3.0.0:
resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
peerDependencies:
prosemirror-model: ^1.22.1
prosemirror-state: ^1.4.2
prosemirror-view: ^1.33.8
prosemirror-transform@1.10.2:
resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==}
prosemirror-view@1.38.0:
resolution: {integrity: sha512-O45kxXQTaP9wPdXhp8TKqCR+/unS/gnfg9Q93svQcB3j0mlp2XSPAmsPefxHADwzC+fbNS404jqRxm3UQaGvgw==}
proto-list@1.2.4:
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
@ -3478,6 +3707,10 @@ packages:
pumpify@1.5.1:
resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@ -3699,6 +3932,9 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@ -3976,6 +4212,9 @@ packages:
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
engines: {node: '>=12.0.0'}
tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@ -4064,6 +4303,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
@ -4116,6 +4358,11 @@ packages:
'@types/react':
optional: true
use-sync-external-store@1.4.0:
resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@ -4190,6 +4437,9 @@ packages:
terser:
optional: true
w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
warning@3.0.0:
resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==}
@ -4899,6 +5149,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@popperjs/core@2.11.8': {}
'@radix-ui/number@1.0.1':
dependencies:
'@babel/runtime': 7.26.7
@ -5384,6 +5636,8 @@ snapshots:
dependencies:
react: 19.0.0
'@remirror/core-constants@3.0.0': {}
'@rollup/rollup-android-arm-eabi@4.32.1':
optional: true
@ -5538,6 +5792,160 @@ snapshots:
'@tanstack/virtual-core@3.13.0': {}
'@tiptap/core@2.11.5(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/pm': 2.11.5
'@tiptap/extension-blockquote@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-bold@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-bubble-menu@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
tippy.js: 6.3.7
'@tiptap/extension-bullet-list@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-code-block@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@tiptap/extension-code@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-document@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-dropcursor@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@tiptap/extension-floating-menu@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
tippy.js: 6.3.7
'@tiptap/extension-gapcursor@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@tiptap/extension-hard-break@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-heading@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-history@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@tiptap/extension-horizontal-rule@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@tiptap/extension-italic@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-list-item@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-ordered-list@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-paragraph@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-strike@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-text-style@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-text@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/pm@2.11.5':
dependencies:
prosemirror-changeset: 2.2.1
prosemirror-collab: 1.3.1
prosemirror-commands: 1.7.0
prosemirror-dropcursor: 1.8.1
prosemirror-gapcursor: 1.3.2
prosemirror-history: 1.4.1
prosemirror-inputrules: 1.4.0
prosemirror-keymap: 1.2.2
prosemirror-markdown: 1.13.1
prosemirror-menu: 1.2.4
prosemirror-model: 1.24.1
prosemirror-schema-basic: 1.2.3
prosemirror-schema-list: 1.5.0
prosemirror-state: 1.4.3
prosemirror-tables: 1.6.4
prosemirror-trailing-node: 3.0.0(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.38.0)
prosemirror-transform: 1.10.2
prosemirror-view: 1.38.0
'@tiptap/react@2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-bubble-menu': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-floating-menu': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/pm': 2.11.5
'@types/use-sync-external-store': 0.0.6
fast-deep-equal: 3.1.3
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
use-sync-external-store: 1.4.0(react@19.0.0)
'@tiptap/starter-kit@2.11.5':
dependencies:
'@tiptap/core': 2.11.5(@tiptap/pm@2.11.5)
'@tiptap/extension-blockquote': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-bold': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-bullet-list': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-code': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-code-block': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-document': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-dropcursor': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-gapcursor': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-hard-break': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-heading': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-history': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-horizontal-rule': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))(@tiptap/pm@2.11.5)
'@tiptap/extension-italic': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-list-item': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-ordered-list': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-paragraph': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-strike': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-text': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/extension-text-style': 2.11.5(@tiptap/core@2.11.5(@tiptap/pm@2.11.5))
'@tiptap/pm': 2.11.5
'@types/conventional-commits-parser@5.0.1':
dependencies:
'@types/node': 20.17.16
@ -5550,6 +5958,15 @@ snapshots:
'@types/json5@0.0.29': {}
'@types/linkify-it@5.0.0': {}
'@types/markdown-it@14.1.2':
dependencies:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
'@types/mdurl@2.0.0': {}
'@types/node@20.17.16':
dependencies:
undici-types: 6.19.8
@ -5566,6 +5983,8 @@ snapshots:
dependencies:
csstype: 3.1.3
'@types/use-sync-external-store@0.0.6': {}
'@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@ -6048,6 +6467,8 @@ snapshots:
optionalDependencies:
typescript: 5.7.3
crelt@1.0.6: {}
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@ -7325,6 +7746,10 @@ snapshots:
lines-and-columns@1.2.4: {}
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
lint-staged@15.4.3:
dependencies:
chalk: 5.4.1
@ -7403,8 +7828,19 @@ snapshots:
lru-cache@7.18.3: {}
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
math-intrinsics@1.1.0: {}
mdurl@2.0.0: {}
media-typer@0.3.0: {}
memoize-one@6.0.0: {}
@ -7600,6 +8036,8 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
orderedmap@2.1.1: {}
own-keys@1.0.1:
dependencies:
get-intrinsic: 1.2.7
@ -7738,6 +8176,109 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
prosemirror-changeset@2.2.1:
dependencies:
prosemirror-transform: 1.10.2
prosemirror-collab@1.3.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-commands@1.7.0:
dependencies:
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-dropcursor@1.8.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-view: 1.38.0
prosemirror-gapcursor@1.3.2:
dependencies:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-view: 1.38.0
prosemirror-history@1.4.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-view: 1.38.0
rope-sequence: 1.3.4
prosemirror-inputrules@1.4.0:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-keymap@1.2.2:
dependencies:
prosemirror-state: 1.4.3
w3c-keyname: 2.2.8
prosemirror-markdown@1.13.1:
dependencies:
'@types/markdown-it': 14.1.2
markdown-it: 14.1.0
prosemirror-model: 1.24.1
prosemirror-menu@1.2.4:
dependencies:
crelt: 1.0.6
prosemirror-commands: 1.7.0
prosemirror-history: 1.4.1
prosemirror-state: 1.4.3
prosemirror-model@1.24.1:
dependencies:
orderedmap: 2.1.1
prosemirror-schema-basic@1.2.3:
dependencies:
prosemirror-model: 1.24.1
prosemirror-schema-list@1.5.0:
dependencies:
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-state@1.4.3:
dependencies:
prosemirror-model: 1.24.1
prosemirror-transform: 1.10.2
prosemirror-view: 1.38.0
prosemirror-tables@1.6.4:
dependencies:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
prosemirror-view: 1.38.0
prosemirror-trailing-node@3.0.0(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.38.0):
dependencies:
'@remirror/core-constants': 3.0.0
escape-string-regexp: 4.0.0
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-view: 1.38.0
prosemirror-transform@1.10.2:
dependencies:
prosemirror-model: 1.24.1
prosemirror-view@1.38.0:
dependencies:
prosemirror-model: 1.24.1
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.2
proto-list@1.2.4: {}
proxy-addr@2.0.7:
@ -7756,6 +8297,8 @@ snapshots:
inherits: 2.0.4
pump: 2.0.1
punycode.js@2.3.1: {}
punycode@2.3.1: {}
qs@6.13.0:
@ -8024,6 +8567,8 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.32.1
fsevents: 2.3.3
rope-sequence@1.3.4: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@ -8337,6 +8882,10 @@ snapshots:
fdir: 6.4.3(picomatch@4.0.2)
picomatch: 4.0.2
tippy.js@6.3.7:
dependencies:
'@popperjs/core': 2.11.8
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@ -8426,6 +8975,8 @@ snapshots:
typescript@5.7.3: {}
uc.micro@2.1.0: {}
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.3
@ -8468,6 +9019,10 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.8
use-sync-external-store@1.4.0(react@19.0.0):
dependencies:
react: 19.0.0
util-deprecate@1.0.2: {}
utils-merge@1.0.1: {}
@ -8526,6 +9081,8 @@ snapshots:
fsevents: 2.3.3
lightningcss: 1.29.1
w3c-keyname@2.2.8: {}
warning@3.0.0:
dependencies:
loose-envify: 1.4.0