mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add link extension support and styling to editor components
This commit is contained in:
parent
1350cb7354
commit
e6d7e881e3
7 changed files with 297 additions and 5 deletions
|
|
@ -32,6 +32,7 @@ import TableHeader from '@tiptap/extension-table-header'
|
||||||
import TableRow from '@tiptap/extension-table-row'
|
import TableRow from '@tiptap/extension-table-row'
|
||||||
import TableCell from '@tiptap/extension-table-cell'
|
import TableCell from '@tiptap/extension-table-cell'
|
||||||
import UserBlock from '@components/Objects/Editor/Extensions/Users/UserBlock'
|
import UserBlock from '@components/Objects/Editor/Extensions/Users/UserBlock'
|
||||||
|
import { getLinkExtension } from '@components/Objects/Editor/EditorConf'
|
||||||
|
|
||||||
interface Editor {
|
interface Editor {
|
||||||
content: string
|
content: string
|
||||||
|
|
@ -112,6 +113,7 @@ function Canva(props: Editor) {
|
||||||
Table.configure({
|
Table.configure({
|
||||||
resizable: true,
|
resizable: true,
|
||||||
}),
|
}),
|
||||||
|
getLinkExtension(),
|
||||||
TableRow,
|
TableRow,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableCell,
|
TableCell,
|
||||||
|
|
@ -194,6 +196,19 @@ const CanvaWrapper = styled.div`
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link styling
|
||||||
|
a {
|
||||||
|
color: #2563eb;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1d4ed8;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ul,
|
ul,
|
||||||
ol {
|
ol {
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ import TableRow from '@tiptap/extension-table-row'
|
||||||
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
|
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
|
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
|
||||||
|
import { getLinkExtension } from './EditorConf'
|
||||||
|
import { Link as LinkExtension } from '@tiptap/extension-link'
|
||||||
|
|
||||||
|
|
||||||
// Lowlight
|
// Lowlight
|
||||||
|
|
@ -151,6 +153,7 @@ function Editor(props: Editor) {
|
||||||
TableRow,
|
TableRow,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableCell,
|
TableCell,
|
||||||
|
getLinkExtension(),
|
||||||
],
|
],
|
||||||
content: props.content,
|
content: props.content,
|
||||||
immediatelyRender: false,
|
immediatelyRender: false,
|
||||||
|
|
@ -459,6 +462,19 @@ export const EditorContentWrapper = styled.div`
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link styling
|
||||||
|
a {
|
||||||
|
color: #2563eb;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1d4ed8;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
|
|
|
||||||
59
apps/web/components/Objects/Editor/EditorConf.ts
Normal file
59
apps/web/components/Objects/Editor/EditorConf.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Link as LinkExtension } from '@tiptap/extension-link'
|
||||||
|
|
||||||
|
export const getLinkExtension = () => {
|
||||||
|
return LinkExtension.configure({
|
||||||
|
openOnClick: true,
|
||||||
|
HTMLAttributes: {
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer',
|
||||||
|
},
|
||||||
|
autolink: true,
|
||||||
|
defaultProtocol: 'https',
|
||||||
|
protocols: ['http', 'https'],
|
||||||
|
isAllowedUri: (url: string, ctx: any) => {
|
||||||
|
try {
|
||||||
|
// construct URL
|
||||||
|
const parsedUrl = url.includes(':') ? new URL(url) : new URL(`${ctx.defaultProtocol}://${url}`)
|
||||||
|
|
||||||
|
// use default validation
|
||||||
|
if (!ctx.defaultValidate(parsedUrl.href)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// disallowed protocols
|
||||||
|
const disallowedProtocols = ['ftp', 'file', 'mailto']
|
||||||
|
const protocol = parsedUrl.protocol.replace(':', '')
|
||||||
|
|
||||||
|
if (disallowedProtocols.includes(protocol)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allow protocols specified in ctx.protocols
|
||||||
|
const allowedProtocols = ctx.protocols.map((p: any) => (typeof p === 'string' ? p : p.scheme))
|
||||||
|
|
||||||
|
if (!allowedProtocols.includes(protocol)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// all checks have passed
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shouldAutoLink: (url: string) => {
|
||||||
|
try {
|
||||||
|
// construct URL
|
||||||
|
const parsedUrl = url.includes(':') ? new URL(url) : new URL(`https://${url}`)
|
||||||
|
|
||||||
|
// only auto-link if the domain is not in the disallowed list
|
||||||
|
const disallowedDomains = ['example-no-autolink.com', 'another-no-autolink.com']
|
||||||
|
const domain = parsedUrl.hostname
|
||||||
|
|
||||||
|
return !disallowedDomains.includes(domain)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
118
apps/web/components/Objects/Editor/Toolbar/LinkInputTooltip.tsx
Normal file
118
apps/web/components/Objects/Editor/Toolbar/LinkInputTooltip.tsx
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { CheckIcon, Cross2Icon } from '@radix-ui/react-icons'
|
||||||
|
|
||||||
|
interface LinkInputTooltipProps {
|
||||||
|
onSave: (url: string) => void
|
||||||
|
onCancel: () => void
|
||||||
|
currentUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkInputTooltip: React.FC<LinkInputTooltipProps> = ({ onSave, onCancel, currentUrl }) => {
|
||||||
|
const [url, setUrl] = useState(currentUrl || '')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUrl(currentUrl || '')
|
||||||
|
}, [currentUrl])
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (url) {
|
||||||
|
// Ensure the URL has a protocol
|
||||||
|
const formattedUrl = url.startsWith('http://') || url.startsWith('https://')
|
||||||
|
? url
|
||||||
|
: `https://${url}`
|
||||||
|
onSave(formattedUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipContainer>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter URL"
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<ButtonGroup>
|
||||||
|
<SaveButton type="submit" disabled={!url}>
|
||||||
|
<CheckIcon />
|
||||||
|
</SaveButton>
|
||||||
|
<CancelButton type="button" onClick={onCancel}>
|
||||||
|
<Cross2Icon />
|
||||||
|
</CancelButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Form>
|
||||||
|
</TooltipContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const TooltipContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid rgba(217, 217, 217, 0.5);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Form = styled.form`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Input = styled.input`
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px solid rgba(217, 217, 217, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(217, 217, 217, 0.8);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ButtonGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(217, 217, 217, 0.24);
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(217, 217, 217, 0.48);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const SaveButton = styled(Button)`
|
||||||
|
color: #4CAF50;
|
||||||
|
`
|
||||||
|
|
||||||
|
const CancelButton = styled(Button)`
|
||||||
|
color: #F44336;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default LinkInputTooltip
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
FileText,
|
FileText,
|
||||||
ImagePlus,
|
ImagePlus,
|
||||||
Lightbulb,
|
Lightbulb,
|
||||||
|
Link2,
|
||||||
MousePointerClick,
|
MousePointerClick,
|
||||||
Sigma,
|
Sigma,
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -34,9 +35,12 @@ import {
|
||||||
import { SiYoutube } from '@icons-pack/react-simple-icons'
|
import { SiYoutube } from '@icons-pack/react-simple-icons'
|
||||||
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
|
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import LinkInputTooltip from './LinkInputTooltip'
|
||||||
|
|
||||||
export const ToolbarButtons = ({ editor, props }: any) => {
|
export const ToolbarButtons = ({ editor, props }: any) => {
|
||||||
const [showTableMenu, setShowTableMenu] = React.useState(false)
|
const [showTableMenu, setShowTableMenu] = React.useState(false)
|
||||||
|
const [showLinkInput, setShowLinkInput] = React.useState(false)
|
||||||
|
const linkButtonRef = React.useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
return null
|
return null
|
||||||
|
|
@ -83,6 +87,47 @@ export const ToolbarButtons = ({ editor, props }: any) => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const handleLinkClick = () => {
|
||||||
|
// Store the current selection
|
||||||
|
const { from, to } = editor.state.selection
|
||||||
|
|
||||||
|
if (editor.isActive('link')) {
|
||||||
|
const currentLink = editor.getAttributes('link')
|
||||||
|
setShowLinkInput(true)
|
||||||
|
} else {
|
||||||
|
setShowLinkInput(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the selection after a small delay to ensure the tooltip is rendered
|
||||||
|
setTimeout(() => {
|
||||||
|
editor.commands.setTextSelection({ from, to })
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCurrentLinkUrl = () => {
|
||||||
|
if (editor.isActive('link')) {
|
||||||
|
return editor.getAttributes('link').href
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLinkSave = (url: string) => {
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.setLink({
|
||||||
|
href: url,
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer'
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
setShowLinkInput(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLinkCancel = () => {
|
||||||
|
setShowLinkInput(false)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolButtonsWrapper>
|
<ToolButtonsWrapper>
|
||||||
<ToolBtn onClick={() => editor.chain().focus().undo().run()}>
|
<ToolBtn onClick={() => editor.chain().focus().undo().run()}>
|
||||||
|
|
@ -185,6 +230,24 @@ export const ToolbarButtons = ({ editor, props }: any) => {
|
||||||
<AlertTriangle size={15} />
|
<AlertTriangle size={15} />
|
||||||
</ToolBtn>
|
</ToolBtn>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
<ToolTip content={'Link'}>
|
||||||
|
<div style={{ position: 'relative' }}>
|
||||||
|
<ToolBtn
|
||||||
|
ref={linkButtonRef}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
className={editor.isActive('link') ? 'is-active' : ''}
|
||||||
|
>
|
||||||
|
<Link2 size={15} />
|
||||||
|
</ToolBtn>
|
||||||
|
{showLinkInput && (
|
||||||
|
<LinkInputTooltip
|
||||||
|
onSave={handleLinkSave}
|
||||||
|
onCancel={handleLinkCancel}
|
||||||
|
currentUrl={getCurrentLinkUrl()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ToolTip>
|
||||||
<ToolTip content={'Image'}>
|
<ToolTip content={'Image'}>
|
||||||
<ToolBtn
|
<ToolBtn
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"@tiptap/core": "^2.11.7",
|
"@tiptap/core": "^2.11.7",
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.11.7",
|
"@tiptap/extension-code-block-lowlight": "^2.11.7",
|
||||||
|
"@tiptap/extension-link": "^2.11.7",
|
||||||
"@tiptap/extension-table": "^2.11.7",
|
"@tiptap/extension-table": "^2.11.7",
|
||||||
"@tiptap/extension-table-cell": "^2.11.7",
|
"@tiptap/extension-table-cell": "^2.11.7",
|
||||||
"@tiptap/extension-table-header": "^2.11.7",
|
"@tiptap/extension-table-header": "^2.11.7",
|
||||||
|
|
|
||||||
28
apps/web/pnpm-lock.yaml
generated
28
apps/web/pnpm-lock.yaml
generated
|
|
@ -81,6 +81,9 @@ importers:
|
||||||
'@tiptap/extension-code-block-lowlight':
|
'@tiptap/extension-code-block-lowlight':
|
||||||
specifier: ^2.11.7
|
specifier: ^2.11.7
|
||||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)
|
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/extension-code-block@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)(highlight.js@11.11.1)(lowlight@3.3.0)
|
||||||
|
'@tiptap/extension-link':
|
||||||
|
specifier: ^2.11.7
|
||||||
|
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||||
'@tiptap/extension-table':
|
'@tiptap/extension-table':
|
||||||
specifier: ^2.11.7
|
specifier: ^2.11.7
|
||||||
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
version: 2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)
|
||||||
|
|
@ -1490,6 +1493,12 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@tiptap/core': ^2.7.0
|
'@tiptap/core': ^2.7.0
|
||||||
|
|
||||||
|
'@tiptap/extension-link@2.11.7':
|
||||||
|
resolution: {integrity: sha512-qKIowE73aAUrnQCIifYP34xXOHOsZw46cT/LBDlb0T60knVfQoKVE4ku08fJzAV+s6zqgsaaZ4HVOXkQYLoW7g==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tiptap/core': ^2.7.0
|
||||||
|
'@tiptap/pm': ^2.7.0
|
||||||
|
|
||||||
'@tiptap/extension-list-item@2.11.7':
|
'@tiptap/extension-list-item@2.11.7':
|
||||||
resolution: {integrity: sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==}
|
resolution: {integrity: sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -2646,6 +2655,9 @@ packages:
|
||||||
linkify-it@5.0.0:
|
linkify-it@5.0.0:
|
||||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||||
|
|
||||||
|
linkifyjs@4.2.0:
|
||||||
|
resolution: {integrity: sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==}
|
||||||
|
|
||||||
load-script@1.0.0:
|
load-script@1.0.0:
|
||||||
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
|
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
|
||||||
|
|
||||||
|
|
@ -3306,8 +3318,8 @@ packages:
|
||||||
tailwind-merge@2.6.0:
|
tailwind-merge@2.6.0:
|
||||||
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
||||||
|
|
||||||
tailwind-merge@3.1.0:
|
tailwind-merge@3.2.0:
|
||||||
resolution: {integrity: sha512-aV27Oj8B7U/tAOMhJsSGdWqelfmudnGMdXIlMnk1JfsjwSjts6o8HyfN7SFH3EztzH4YH8kk6GbLTHzITJO39Q==}
|
resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==}
|
||||||
|
|
||||||
tailwindcss-animate@1.0.7:
|
tailwindcss-animate@1.0.7:
|
||||||
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
|
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
|
||||||
|
|
@ -4665,6 +4677,12 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||||
|
|
||||||
|
'@tiptap/extension-link@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))(@tiptap/pm@2.11.7)':
|
||||||
|
dependencies:
|
||||||
|
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||||
|
'@tiptap/pm': 2.11.7
|
||||||
|
linkifyjs: 4.2.0
|
||||||
|
|
||||||
'@tiptap/extension-list-item@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
|
'@tiptap/extension-list-item@2.11.7(@tiptap/core@2.11.7(@tiptap/pm@2.11.7))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
'@tiptap/core': 2.11.7(@tiptap/pm@2.11.7)
|
||||||
|
|
@ -5280,7 +5298,7 @@ snapshots:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
react-dom: 19.0.0(react@19.0.0)
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
react-easy-sort: 1.6.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
react-easy-sort: 1.6.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
tailwind-merge: 3.1.0
|
tailwind-merge: 3.2.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
|
|
@ -6012,6 +6030,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
uc.micro: 2.1.0
|
uc.micro: 2.1.0
|
||||||
|
|
||||||
|
linkifyjs@4.2.0: {}
|
||||||
|
|
||||||
load-script@1.0.0: {}
|
load-script@1.0.0: {}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
|
|
@ -6772,7 +6792,7 @@ snapshots:
|
||||||
|
|
||||||
tailwind-merge@2.6.0: {}
|
tailwind-merge@2.6.0: {}
|
||||||
|
|
||||||
tailwind-merge@3.1.0: {}
|
tailwind-merge@3.2.0: {}
|
||||||
|
|
||||||
tailwindcss-animate@1.0.7(tailwindcss@4.1.3):
|
tailwindcss-animate@1.0.7(tailwindcss@4.1.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue