mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-12 02:58:59 +08:00
Message rendering (#6868)
Co-authored-by: luowei <glpat-EjySCyNjWiLqAED-YmwM> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
This commit is contained in:
parent
dadca0f91a
commit
3230f4a0ec
119
web/app/components/base/audio-gallery/AudioPlayer.module.css
Normal file
119
web/app/components/base/audio-gallery/AudioPlayer.module.css
Normal file
@ -0,0 +1,119 @@
|
||||
.audioPlayer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
min-width: 240px;
|
||||
max-width: 420px;
|
||||
max-height: 40px;
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(16, 24, 40, 0.08);
|
||||
box-shadow: 0 1px 2px rgba(9, 9, 11, 0.05);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.playButton {
|
||||
display: inline-flex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background-color: #296DFF;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.1s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.playButton:hover {
|
||||
background-color: #3367d6;
|
||||
}
|
||||
|
||||
.playButton:disabled {
|
||||
background-color: #bdbdbf;
|
||||
}
|
||||
|
||||
.audioControls {
|
||||
flex-grow: 1;
|
||||
|
||||
}
|
||||
|
||||
.progressBarContainer {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.waveform {
|
||||
position: relative;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.5;
|
||||
border-radius: 2px;
|
||||
flex: none;
|
||||
order: 55;
|
||||
flex-grow: 0;
|
||||
height: 100%;
|
||||
background-color: rgba(66, 133, 244, 0.3);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.timeDisplay {
|
||||
/* position: absolute; */
|
||||
color: #296DFF;
|
||||
border-radius: 2px;
|
||||
order: 0;
|
||||
height: 100%;
|
||||
width: 50px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* .currentTime {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 5px);
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(255,255,255,.8);
|
||||
padding: 2px 4px;
|
||||
border-radius:10px;
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08);
|
||||
} */
|
||||
|
||||
.duration {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 2px 4px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.source_unavailable {
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
color: #bdbdbf;
|
||||
}
|
||||
|
||||
.playButton svg path,
|
||||
.playButton svg rect{
|
||||
fill:currentColor;
|
||||
}
|
320
web/app/components/base/audio-gallery/AudioPlayer.tsx
Normal file
320
web/app/components/base/audio-gallery/AudioPlayer.tsx
Normal file
@ -0,0 +1,320 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { t } from 'i18next'
|
||||
import styles from './AudioPlayer.module.css'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
|
||||
type AudioPlayerProps = {
|
||||
src: string
|
||||
}
|
||||
|
||||
const AudioPlayer: React.FC<AudioPlayerProps> = ({ src }) => {
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [duration, setDuration] = useState(0)
|
||||
const [waveformData, setWaveformData] = useState<number[]>([])
|
||||
const [bufferedTime, setBufferedTime] = useState(0)
|
||||
const audioRef = useRef<HTMLAudioElement>(null)
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const [hasStartedPlaying, setHasStartedPlaying] = useState(false)
|
||||
const [hoverTime, setHoverTime] = useState(0)
|
||||
const [isAudioAvailable, setIsAudioAvailable] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current
|
||||
if (!audio)
|
||||
return
|
||||
|
||||
const handleError = () => {
|
||||
setIsAudioAvailable(false)
|
||||
}
|
||||
|
||||
const setAudioData = () => {
|
||||
setDuration(audio.duration)
|
||||
}
|
||||
|
||||
const setAudioTime = () => {
|
||||
setCurrentTime(audio.currentTime)
|
||||
}
|
||||
|
||||
const handleProgress = () => {
|
||||
if (audio.buffered.length > 0)
|
||||
setBufferedTime(audio.buffered.end(audio.buffered.length - 1))
|
||||
}
|
||||
|
||||
const handleEnded = () => {
|
||||
setIsPlaying(false)
|
||||
}
|
||||
|
||||
audio.addEventListener('loadedmetadata', setAudioData)
|
||||
audio.addEventListener('timeupdate', setAudioTime)
|
||||
audio.addEventListener('progress', handleProgress)
|
||||
audio.addEventListener('ended', handleEnded)
|
||||
audio.addEventListener('error', handleError)
|
||||
|
||||
// Preload audio metadata
|
||||
audio.load()
|
||||
|
||||
// Delayed generation of waveform data
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
const timer = setTimeout(() => generateWaveformData(src), 1000)
|
||||
|
||||
return () => {
|
||||
audio.removeEventListener('loadedmetadata', setAudioData)
|
||||
audio.removeEventListener('timeupdate', setAudioTime)
|
||||
audio.removeEventListener('progress', handleProgress)
|
||||
audio.removeEventListener('ended', handleEnded)
|
||||
audio.removeEventListener('error', handleError)
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [src])
|
||||
|
||||
const generateWaveformData = async (audioSrc: string) => {
|
||||
if (!window.AudioContext && !(window as any).webkitAudioContext) {
|
||||
setIsAudioAvailable(false)
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: 'Web Audio API is not supported in this browser',
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
const url = new URL(src)
|
||||
const isHttp = url.protocol === 'http:' || url.protocol === 'https:'
|
||||
if (!isHttp) {
|
||||
setIsAudioAvailable(false)
|
||||
return null
|
||||
}
|
||||
|
||||
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
|
||||
const samples = 70
|
||||
|
||||
try {
|
||||
const response = await fetch(audioSrc, { mode: 'cors' })
|
||||
if (!response || !response.ok) {
|
||||
setIsAudioAvailable(false)
|
||||
return null
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
|
||||
const channelData = audioBuffer.getChannelData(0)
|
||||
const blockSize = Math.floor(channelData.length / samples)
|
||||
const waveformData: number[] = []
|
||||
|
||||
for (let i = 0; i < samples; i++) {
|
||||
let sum = 0
|
||||
for (let j = 0; j < blockSize; j++)
|
||||
sum += Math.abs(channelData[i * blockSize + j])
|
||||
|
||||
// Apply nonlinear scaling to enhance small amplitudes
|
||||
waveformData.push((sum / blockSize) * 5)
|
||||
}
|
||||
|
||||
// Normalized waveform data
|
||||
const maxAmplitude = Math.max(...waveformData)
|
||||
const normalizedWaveform = waveformData.map(amp => amp / maxAmplitude)
|
||||
|
||||
setWaveformData(normalizedWaveform)
|
||||
setIsAudioAvailable(true)
|
||||
}
|
||||
catch (error) {
|
||||
const waveform: number[] = []
|
||||
let prevValue = Math.random()
|
||||
|
||||
for (let i = 0; i < samples; i++) {
|
||||
const targetValue = Math.random()
|
||||
const interpolatedValue = prevValue + (targetValue - prevValue) * 0.3
|
||||
waveform.push(interpolatedValue)
|
||||
prevValue = interpolatedValue
|
||||
}
|
||||
|
||||
const maxAmplitude = Math.max(...waveform)
|
||||
const randomWaveform = waveform.map(amp => amp / maxAmplitude)
|
||||
|
||||
setWaveformData(randomWaveform)
|
||||
setIsAudioAvailable(true)
|
||||
}
|
||||
finally {
|
||||
await audioContext.close()
|
||||
}
|
||||
}
|
||||
|
||||
const togglePlay = useCallback(() => {
|
||||
const audio = audioRef.current
|
||||
if (audio && isAudioAvailable) {
|
||||
if (isPlaying) {
|
||||
setHasStartedPlaying(false)
|
||||
audio.pause()
|
||||
}
|
||||
else {
|
||||
setHasStartedPlaying(true)
|
||||
audio.play().catch(error => console.error('Error playing audio:', error))
|
||||
}
|
||||
|
||||
setIsPlaying(!isPlaying)
|
||||
}
|
||||
else {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: 'Audio element not found',
|
||||
})
|
||||
setIsAudioAvailable(false)
|
||||
}
|
||||
}, [isAudioAvailable, isPlaying])
|
||||
|
||||
const handleCanvasInteraction = useCallback((e: React.MouseEvent | React.TouchEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
const getClientX = (event: React.MouseEvent | React.TouchEvent): number => {
|
||||
if ('touches' in event)
|
||||
return event.touches[0].clientX
|
||||
return event.clientX
|
||||
}
|
||||
|
||||
const updateProgress = (clientX: number) => {
|
||||
const canvas = canvasRef.current
|
||||
const audio = audioRef.current
|
||||
if (!canvas || !audio)
|
||||
return
|
||||
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
const percent = Math.min(Math.max(0, clientX - rect.left), rect.width) / rect.width
|
||||
const newTime = percent * duration
|
||||
|
||||
// Removes the buffer check, allowing drag to any location
|
||||
audio.currentTime = newTime
|
||||
setCurrentTime(newTime)
|
||||
|
||||
if (!isPlaying) {
|
||||
setIsPlaying(true)
|
||||
audio.play().catch((error) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: `Error playing audio: ${error}`,
|
||||
})
|
||||
setIsPlaying(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateProgress(getClientX(e))
|
||||
}, [duration, isPlaying])
|
||||
|
||||
const formatTime = (time: number) => {
|
||||
const minutes = Math.floor(time / 60)
|
||||
const seconds = Math.floor(time % 60)
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const drawWaveform = useCallback(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas)
|
||||
return
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx)
|
||||
return
|
||||
|
||||
const width = canvas.width
|
||||
const height = canvas.height
|
||||
const data = waveformData
|
||||
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
const barWidth = width / data.length
|
||||
const playedWidth = (currentTime / duration) * width
|
||||
const cornerRadius = 2
|
||||
|
||||
// Draw waveform bars
|
||||
data.forEach((value, index) => {
|
||||
let color
|
||||
|
||||
if (index * barWidth <= playedWidth)
|
||||
color = '#296DFF'
|
||||
else if ((index * barWidth / width) * duration <= hoverTime)
|
||||
color = 'rgba(21,90,239,.40)'
|
||||
else
|
||||
color = 'rgba(21,90,239,.20)'
|
||||
|
||||
const barHeight = value * height
|
||||
const rectX = index * barWidth
|
||||
const rectY = (height - barHeight) / 2
|
||||
const rectWidth = barWidth * 0.5
|
||||
const rectHeight = barHeight
|
||||
|
||||
ctx.lineWidth = 1
|
||||
ctx.fillStyle = color
|
||||
if (ctx.roundRect) {
|
||||
ctx.beginPath()
|
||||
ctx.roundRect(rectX, rectY, rectWidth, rectHeight, cornerRadius)
|
||||
ctx.fill()
|
||||
}
|
||||
else {
|
||||
ctx.fillRect(rectX, rectY, rectWidth, rectHeight)
|
||||
}
|
||||
})
|
||||
}, [currentTime, duration, hoverTime, waveformData])
|
||||
|
||||
useEffect(() => {
|
||||
drawWaveform()
|
||||
}, [drawWaveform, bufferedTime, hasStartedPlaying])
|
||||
|
||||
const handleMouseMove = useCallback((e: React.MouseEvent) => {
|
||||
const canvas = canvasRef.current
|
||||
const audio = audioRef.current
|
||||
if (!canvas || !audio)
|
||||
return
|
||||
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
const percent = Math.min(Math.max(0, e.clientX - rect.left), rect.width) / rect.width
|
||||
const time = percent * duration
|
||||
|
||||
// Check if the hovered position is within a buffered range before updating hoverTime
|
||||
for (let i = 0; i < audio.buffered.length; i++) {
|
||||
if (time >= audio.buffered.start(i) && time <= audio.buffered.end(i)) {
|
||||
setHoverTime(time)
|
||||
break
|
||||
}
|
||||
}
|
||||
}, [duration])
|
||||
|
||||
return (
|
||||
<div className={styles.audioPlayer}>
|
||||
<audio ref={audioRef} src={src} preload="auto"/>
|
||||
<button className={styles.playButton} onClick={togglePlay} disabled={!isAudioAvailable}>
|
||||
{isPlaying
|
||||
? (
|
||||
<svg viewBox="0 0 24 24" width="16" height="16">
|
||||
<rect x="7" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
|
||||
<rect x="15" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
|
||||
</svg>
|
||||
)
|
||||
: (
|
||||
<svg viewBox="0 0 24 24" width="16" height="16">
|
||||
<path d="M8 5v14l11-7z" fill="currentColor"/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
<div className={isAudioAvailable ? styles.audioControls : styles.audioControls_disabled} hidden={!isAudioAvailable}>
|
||||
<div className={styles.progressBarContainer}>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className={styles.waveform}
|
||||
onClick={handleCanvasInteraction}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseDown={handleCanvasInteraction}
|
||||
/>
|
||||
{/* <div className={styles.currentTime} style={{ left: `${(currentTime / duration) * 81}%`, bottom: '29px' }}>
|
||||
{formatTime(currentTime)}
|
||||
</div> */}
|
||||
<div className={styles.timeDisplay}>
|
||||
<span className={styles.duration}>{formatTime(duration)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.source_unavailable} hidden={isAudioAvailable}>{t('common.operation.audioSourceUnavailable')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AudioPlayer
|
12
web/app/components/base/audio-gallery/index.tsx
Normal file
12
web/app/components/base/audio-gallery/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react'
|
||||
import AudioPlayer from './AudioPlayer'
|
||||
|
||||
type Props = {
|
||||
srcs: string[]
|
||||
}
|
||||
|
||||
const AudioGallery: React.FC<Props> = ({ srcs }) => {
|
||||
return (<><br/>{srcs.map((src, index) => (<AudioPlayer key={`audio_${index}`} src={src}/>))}</>)
|
||||
}
|
||||
|
||||
export default React.memo(AudioGallery)
|
38
web/app/components/base/image-uploader/audio-preview.tsx
Normal file
38
web/app/components/base/image-uploader/audio-preview.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import type { FC } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
|
||||
type AudioPreviewProps = {
|
||||
url: string
|
||||
title: string
|
||||
onCancel: () => void
|
||||
}
|
||||
const AudioPreview: FC<AudioPreviewProps> = ({
|
||||
url,
|
||||
title,
|
||||
onCancel,
|
||||
}) => {
|
||||
return createPortal(
|
||||
<div className='fixed inset-0 p-8 flex items-center justify-center bg-black/80 z-[1000]' onClick={e => e.stopPropagation()}>
|
||||
<div>
|
||||
<audio controls title={title} autoPlay={false} preload="metadata">
|
||||
<source
|
||||
type="audio/mpeg"
|
||||
src={url}
|
||||
className='max-w-full max-h-full'
|
||||
/>
|
||||
</audio>
|
||||
</div>
|
||||
<div
|
||||
className='absolute top-6 right-6 flex items-center justify-center w-8 h-8 bg-white/[0.08] rounded-lg backdrop-blur-[2px] cursor-pointer'
|
||||
onClick={onCancel}
|
||||
>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500'/>
|
||||
</div>
|
||||
</div>
|
||||
,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
export default AudioPreview
|
@ -1,19 +1,43 @@
|
||||
import type { FC } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { t } from 'i18next'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { RiCloseLine, RiExternalLinkLine } from '@remixicon/react'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { randomString } from '@/utils'
|
||||
|
||||
type ImagePreviewProps = {
|
||||
url: string
|
||||
title: string
|
||||
onCancel: () => void
|
||||
}
|
||||
const ImagePreview: FC<ImagePreviewProps> = ({
|
||||
url,
|
||||
title,
|
||||
onCancel,
|
||||
}) => {
|
||||
const selector = useRef(`copy-tooltip-${randomString(4)}`)
|
||||
|
||||
const openInNewTab = () => {
|
||||
// Open in a new window, considering the case when the page is inside an iframe
|
||||
if (url.startsWith('http')) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
else if (url.startsWith('data:image')) {
|
||||
// Base64 image
|
||||
const win = window.open()
|
||||
win?.document.write(`<img src="${url}" alt="${title}" />`)
|
||||
}
|
||||
else {
|
||||
console.error('Unable to open image', url)
|
||||
}
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<div className='fixed inset-0 p-8 flex items-center justify-center bg-black/80 z-[1000]' onClick={e => e.stopPropagation()}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
alt='preview image'
|
||||
alt={title}
|
||||
src={url}
|
||||
className='max-w-full max-h-full'
|
||||
/>
|
||||
@ -23,6 +47,18 @@ const ImagePreview: FC<ImagePreviewProps> = ({
|
||||
>
|
||||
<RiCloseLine className='w-4 h-4 text-white' />
|
||||
</div>
|
||||
<Tooltip
|
||||
selector={selector.current}
|
||||
content={(t('common.operation.openInNewTab') ?? 'Open in new tab')}
|
||||
className='z-10'
|
||||
>
|
||||
<div
|
||||
className='absolute top-6 right-16 flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer'
|
||||
onClick={openInNewTab}
|
||||
>
|
||||
<RiExternalLinkLine className='w-4 h-4 text-white' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
|
38
web/app/components/base/image-uploader/video-preview.tsx
Normal file
38
web/app/components/base/image-uploader/video-preview.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import type { FC } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
|
||||
type VideoPreviewProps = {
|
||||
url: string
|
||||
title: string
|
||||
onCancel: () => void
|
||||
}
|
||||
const VideoPreview: FC<VideoPreviewProps> = ({
|
||||
url,
|
||||
title,
|
||||
onCancel,
|
||||
}) => {
|
||||
return createPortal(
|
||||
<div className='fixed inset-0 p-8 flex items-center justify-center bg-black/80 z-[1000]' onClick={e => e.stopPropagation()}>
|
||||
<div>
|
||||
<video controls title={title} autoPlay={false} preload="metadata">
|
||||
<source
|
||||
type="video/mp4"
|
||||
src={url}
|
||||
className='max-w-full max-h-full'
|
||||
/>
|
||||
</video>
|
||||
</div>
|
||||
<div
|
||||
className='absolute top-6 right-6 flex items-center justify-center w-8 h-8 bg-white/[0.08] rounded-lg backdrop-blur-[2px] cursor-pointer'
|
||||
onClick={onCancel}
|
||||
>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500'/>
|
||||
</div>
|
||||
</div>
|
||||
,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoPreview
|
@ -4,6 +4,7 @@ import 'katex/dist/katex.min.css'
|
||||
import RemarkMath from 'remark-math'
|
||||
import RemarkBreaks from 'remark-breaks'
|
||||
import RehypeKatex from 'rehype-katex'
|
||||
import RehypeRaw from 'rehype-raw'
|
||||
import RemarkGfm from 'remark-gfm'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'
|
||||
@ -15,6 +16,9 @@ import CopyBtn from '@/app/components/base/copy-btn'
|
||||
import SVGBtn from '@/app/components/base/svg'
|
||||
import Flowchart from '@/app/components/base/mermaid'
|
||||
import ImageGallery from '@/app/components/base/image-gallery'
|
||||
import { useChatContext } from '@/app/components/base/chat/chat/context'
|
||||
import VideoGallery from '@/app/components/base/video-gallery'
|
||||
import AudioGallery from '@/app/components/base/audio-gallery'
|
||||
|
||||
// Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
|
||||
const capitalizationLanguageNameMap: Record<string, string> = {
|
||||
@ -33,6 +37,10 @@ const capitalizationLanguageNameMap: Record<string, string> = {
|
||||
markdown: 'MarkDown',
|
||||
makefile: 'MakeFile',
|
||||
echarts: 'ECharts',
|
||||
shell: 'Shell',
|
||||
powershell: 'PowerShell',
|
||||
json: 'JSON',
|
||||
latex: 'Latex',
|
||||
}
|
||||
const getCorrectCapitalizationLanguageName = (language: string) => {
|
||||
if (!language)
|
||||
@ -65,6 +73,7 @@ export function PreCode(props: { children: any }) {
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
const useLazyLoad = (ref: RefObject<Element>): boolean => {
|
||||
const [isIntersecting, setIntersecting] = useState<boolean>(false)
|
||||
|
||||
@ -126,12 +135,7 @@ const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }
|
||||
>
|
||||
<div className='text-[13px] text-gray-500 font-normal'>{languageShowName}</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
{language === 'mermaid'
|
||||
&& <SVGBtn
|
||||
isSVG={isSVG}
|
||||
setIsSVG={setIsSVG}
|
||||
/>
|
||||
}
|
||||
{language === 'mermaid' && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG} />}
|
||||
<CopyBtn
|
||||
className='mr-1'
|
||||
value={String(children).replace(/\n$/, '')}
|
||||
@ -172,36 +176,83 @@ const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }
|
||||
|
||||
CodeBlock.displayName = 'CodeBlock'
|
||||
|
||||
const VideoBlock: CodeComponent = memo(({ node }) => {
|
||||
const srcs = node.children.filter(child => 'properties' in child).map(child => (child as any).properties.src)
|
||||
if (srcs.length === 0)
|
||||
return null
|
||||
return <VideoGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
VideoBlock.displayName = 'VideoBlock'
|
||||
|
||||
const AudioBlock: CodeComponent = memo(({ node }) => {
|
||||
const srcs = node.children.filter(child => 'properties' in child).map(child => (child as any).properties.src)
|
||||
if (srcs.length === 0)
|
||||
return null
|
||||
return <AudioGallery key={srcs.join()} srcs={srcs} />
|
||||
})
|
||||
AudioBlock.displayName = 'AudioBlock'
|
||||
|
||||
const Paragraph = (paragraph: any) => {
|
||||
const { node }: any = paragraph
|
||||
const children_node = node.children
|
||||
if (children_node && children_node[0] && 'tagName' in children_node[0] && children_node[0].tagName === 'img') {
|
||||
return (
|
||||
<>
|
||||
<ImageGallery srcs={[children_node[0].properties.src]} />
|
||||
<div>{paragraph.children.slice(1)}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return <div>{paragraph.children}</div>
|
||||
}
|
||||
|
||||
const Img = ({ src }: any) => {
|
||||
return (<ImageGallery srcs={[src]} />)
|
||||
}
|
||||
|
||||
const Link = ({ node, ...props }: any) => {
|
||||
if (node.properties?.href && node.properties.href?.toString().startsWith('abbr')) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { onSend } = useChatContext()
|
||||
const hidden_text = decodeURIComponent(node.properties.href.toString().split('abbr:')[1])
|
||||
|
||||
return <abbr className="underline decoration-dashed !decoration-primary-700 cursor-pointer" onClick={() => onSend?.(hidden_text)} title={node.children[0]?.value}>{node.children[0]?.value}</abbr>
|
||||
}
|
||||
else {
|
||||
return <a {...props} target="_blank" className="underline decoration-dashed !decoration-primary-700 cursor-pointer">{node.children[0] ? node.children[0]?.value : 'Download'}</a>
|
||||
}
|
||||
}
|
||||
|
||||
export function Markdown(props: { content: string; className?: string }) {
|
||||
const latexContent = preprocessLaTeX(props.content)
|
||||
return (
|
||||
<div className={cn(props.className, 'markdown-body')}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[[RemarkMath, { singleDollarTextMath: false }], RemarkGfm, RemarkBreaks]}
|
||||
remarkPlugins={[[RemarkGfm, RemarkMath, { singleDollarTextMath: false }], RemarkBreaks]}
|
||||
rehypePlugins={[
|
||||
RehypeKatex as any,
|
||||
RehypeKatex,
|
||||
RehypeRaw as any,
|
||||
// The Rehype plug-in is used to remove the ref attribute of an element
|
||||
() => {
|
||||
return (tree) => {
|
||||
const iterate = (node: any) => {
|
||||
if (node.type === 'element' && !node.properties?.src && node.properties?.ref && node.properties.ref.startsWith('{') && node.properties.ref.endsWith('}'))
|
||||
delete node.properties.ref
|
||||
|
||||
if (node.children)
|
||||
node.children.forEach(iterate)
|
||||
}
|
||||
tree.children.forEach(iterate)
|
||||
}
|
||||
},
|
||||
]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img({ src }) {
|
||||
return (
|
||||
<ImageGallery srcs={[src || '']} />
|
||||
)
|
||||
},
|
||||
p: (paragraph) => {
|
||||
const { node }: any = paragraph
|
||||
if (node.children[0].tagName === 'img') {
|
||||
const image = node.children[0]
|
||||
|
||||
return (
|
||||
<>
|
||||
<ImageGallery srcs={[image.properties.src]} />
|
||||
<p>{paragraph.children.slice(1)}</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return <p>{paragraph.children}</p>
|
||||
},
|
||||
img: Img,
|
||||
video: VideoBlock,
|
||||
audio: AudioBlock,
|
||||
a: Link,
|
||||
p: Paragraph,
|
||||
}}
|
||||
linkTarget='_blank'
|
||||
>
|
||||
|
188
web/app/components/base/video-gallery/VideoPlayer.module.css
Normal file
188
web/app/components/base/video-gallery/VideoPlayer.module.css
Normal file
@ -0,0 +1,188 @@
|
||||
.videoPlayer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.controls.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.controls.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, transparent 100%);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.progressBarContainer {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.controlsContent {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.leftControls, .rightControls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.playPauseButton, .muteButton, .fullscreenButton {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.playPauseButton:hover, .muteButton:hover, .fullscreenButton:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.volumeControl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.volumeSlider {
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
margin-left: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.volumeLevel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
overflow: visible;
|
||||
transition: height 0.2s ease;
|
||||
}
|
||||
|
||||
.progressBar:hover {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
transition: width 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.hoverTimeIndicator {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hoverTimeIndicator::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -4px;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
border-color: rgba(0, 0, 0, 0.7) transparent transparent transparent;
|
||||
}
|
||||
|
||||
.controls.smallSize .controlsContent {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.controls.smallSize .leftControls,
|
||||
.controls.smallSize .rightControls {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.controls.smallSize .rightControls {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.controls.smallSize .progressBarContainer {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.controls.smallSize .playPauseButton,
|
||||
.controls.smallSize .muteButton,
|
||||
.controls.smallSize .fullscreenButton {
|
||||
padding: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.controls.smallSize .playPauseButton svg,
|
||||
.controls.smallSize .muteButton svg,
|
||||
.controls.smallSize .fullscreenButton svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.controls.smallSize .muteButton {
|
||||
order: -1;
|
||||
}
|
278
web/app/components/base/video-gallery/VideoPlayer.tsx
Normal file
278
web/app/components/base/video-gallery/VideoPlayer.tsx
Normal file
@ -0,0 +1,278 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import styles from './VideoPlayer.module.css'
|
||||
|
||||
type VideoPlayerProps = {
|
||||
src: string
|
||||
}
|
||||
|
||||
const PlayIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 5V19L19 12L8 5Z" fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const PauseIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 19H10V5H6V19ZM14 5V19H18V5H14Z" fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const MuteIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 9V15H7L12 20V4L7 9H3ZM16.5 12C16.5 10.23 15.48 8.71 14 7.97V16.02C15.48 15.29 16.5 13.77 16.5 12ZM14 3.23V5.29C16.89 6.15 19 8.83 19 12C19 15.17 16.89 17.85 14 18.71V20.77C18.01 19.86 21 16.28 21 12C21 7.72 18.01 4.14 14 3.23Z" fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const UnmuteIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.34 2.93L2.93 4.34L7.29 8.7L7 9H3V15H7L12 20V13.41L16.18 17.59C15.69 17.96 15.16 18.27 14.58 18.5V20.58C15.94 20.22 17.15 19.56 18.13 18.67L19.66 20.2L21.07 18.79L4.34 2.93ZM10 15.17L7.83 13H5V11H7.83L10 8.83V15.17ZM19 12C19 12.82 18.85 13.61 18.59 14.34L20.12 15.87C20.68 14.7 21 13.39 21 12C21 7.72 18.01 4.14 14 3.23V5.29C16.89 6.15 19 8.83 19 12ZM12 4L10.12 5.88L12 7.76V4ZM16.5 12C16.5 10.23 15.48 8.71 14 7.97V10.18L16.45 12.63C16.48 12.43 16.5 12.22 16.5 12Z" fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const FullscreenIcon = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 14H5V19H10V17H7V14ZM5 10H7V7H10V5H5V10ZM17 17H14V19H19V14H17V17ZM14 5V7H17V10H19V5H14Z" fill="currentColor"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const VideoPlayer: React.FC<VideoPlayerProps> = ({ src }) => {
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [duration, setDuration] = useState(0)
|
||||
const [isMuted, setIsMuted] = useState(false)
|
||||
const [volume, setVolume] = useState(1)
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [isControlsVisible, setIsControlsVisible] = useState(true)
|
||||
const [hoverTime, setHoverTime] = useState<number | null>(null)
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const progressRef = useRef<HTMLDivElement>(null)
|
||||
const volumeRef = useRef<HTMLDivElement>(null)
|
||||
const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const [isSmallSize, setIsSmallSize] = useState(false)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const video = videoRef.current
|
||||
if (!video)
|
||||
return
|
||||
|
||||
const setVideoData = () => {
|
||||
setDuration(video.duration)
|
||||
setVolume(video.volume)
|
||||
}
|
||||
|
||||
const setVideoTime = () => {
|
||||
setCurrentTime(video.currentTime)
|
||||
}
|
||||
|
||||
const handleEnded = () => {
|
||||
setIsPlaying(false)
|
||||
}
|
||||
|
||||
video.addEventListener('loadedmetadata', setVideoData)
|
||||
video.addEventListener('timeupdate', setVideoTime)
|
||||
video.addEventListener('ended', handleEnded)
|
||||
|
||||
return () => {
|
||||
video.removeEventListener('loadedmetadata', setVideoData)
|
||||
video.removeEventListener('timeupdate', setVideoTime)
|
||||
video.removeEventListener('ended', handleEnded)
|
||||
}
|
||||
}, [src])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (controlsTimeoutRef.current)
|
||||
clearTimeout(controlsTimeoutRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const showControls = useCallback(() => {
|
||||
setIsControlsVisible(true)
|
||||
if (controlsTimeoutRef.current)
|
||||
clearTimeout(controlsTimeoutRef.current)
|
||||
|
||||
controlsTimeoutRef.current = setTimeout(() => setIsControlsVisible(false), 3000)
|
||||
}, [])
|
||||
|
||||
const togglePlayPause = useCallback(() => {
|
||||
const video = videoRef.current
|
||||
if (video) {
|
||||
if (isPlaying)
|
||||
video.pause()
|
||||
else video.play().catch(error => console.error('Error playing video:', error))
|
||||
setIsPlaying(!isPlaying)
|
||||
}
|
||||
}, [isPlaying])
|
||||
|
||||
const toggleMute = useCallback(() => {
|
||||
const video = videoRef.current
|
||||
if (video) {
|
||||
const newMutedState = !video.muted
|
||||
video.muted = newMutedState
|
||||
setIsMuted(newMutedState)
|
||||
setVolume(newMutedState ? 0 : (video.volume > 0 ? video.volume : 1))
|
||||
video.volume = newMutedState ? 0 : (video.volume > 0 ? video.volume : 1)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
const video = videoRef.current
|
||||
if (video) {
|
||||
if (document.fullscreenElement)
|
||||
document.exitFullscreen()
|
||||
else video.requestFullscreen()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const formatTime = (time: number) => {
|
||||
const minutes = Math.floor(time / 60)
|
||||
const seconds = Math.floor(time % 60)
|
||||
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const updateVideoProgress = useCallback((clientX: number) => {
|
||||
const progressBar = progressRef.current
|
||||
const video = videoRef.current
|
||||
if (progressBar && video) {
|
||||
const rect = progressBar.getBoundingClientRect()
|
||||
const pos = (clientX - rect.left) / rect.width
|
||||
const newTime = pos * video.duration
|
||||
if (newTime >= 0 && newTime <= video.duration) {
|
||||
setHoverTime(newTime)
|
||||
if (isDragging)
|
||||
video.currentTime = newTime
|
||||
}
|
||||
}
|
||||
}, [isDragging])
|
||||
|
||||
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
updateVideoProgress(e.clientX)
|
||||
}, [updateVideoProgress])
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
if (!isDragging)
|
||||
setHoverTime(null)
|
||||
}, [isDragging])
|
||||
|
||||
const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(true)
|
||||
updateVideoProgress(e.clientX)
|
||||
}, [updateVideoProgress])
|
||||
|
||||
useEffect(() => {
|
||||
const handleGlobalMouseMove = (e: MouseEvent) => {
|
||||
if (isDragging)
|
||||
updateVideoProgress(e.clientX)
|
||||
}
|
||||
|
||||
const handleGlobalMouseUp = () => {
|
||||
setIsDragging(false)
|
||||
setHoverTime(null)
|
||||
}
|
||||
|
||||
if (isDragging) {
|
||||
document.addEventListener('mousemove', handleGlobalMouseMove)
|
||||
document.addEventListener('mouseup', handleGlobalMouseUp)
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleGlobalMouseMove)
|
||||
document.removeEventListener('mouseup', handleGlobalMouseUp)
|
||||
}
|
||||
}, [isDragging, updateVideoProgress])
|
||||
|
||||
const checkSize = useCallback(() => {
|
||||
if (containerRef.current)
|
||||
setIsSmallSize(containerRef.current.offsetWidth < 400)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
checkSize()
|
||||
window.addEventListener('resize', checkSize)
|
||||
return () => window.removeEventListener('resize', checkSize)
|
||||
}, [checkSize])
|
||||
|
||||
const handleVolumeChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const volumeBar = volumeRef.current
|
||||
const video = videoRef.current
|
||||
if (volumeBar && video) {
|
||||
const rect = volumeBar.getBoundingClientRect()
|
||||
const newVolume = (e.clientX - rect.left) / rect.width
|
||||
const clampedVolume = Math.max(0, Math.min(1, newVolume))
|
||||
video.volume = clampedVolume
|
||||
setVolume(clampedVolume)
|
||||
setIsMuted(clampedVolume === 0)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={styles.videoPlayer} onMouseMove={showControls} onMouseEnter={showControls}>
|
||||
<video ref={videoRef} src={src} className={styles.video} />
|
||||
<div className={`${styles.controls} ${isControlsVisible ? styles.visible : styles.hidden} ${isSmallSize ? styles.smallSize : ''}`}>
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.progressBarContainer}>
|
||||
<div
|
||||
ref={progressRef}
|
||||
className={styles.progressBar}
|
||||
onClick={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
<div className={styles.progress} style={{ width: `${(currentTime / duration) * 100}%` }} />
|
||||
{hoverTime !== null && (
|
||||
<div
|
||||
className={styles.hoverTimeIndicator}
|
||||
style={{ left: `${(hoverTime / duration) * 100}%` }}
|
||||
>
|
||||
{formatTime(hoverTime)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.controlsContent}>
|
||||
<div className={styles.leftControls}>
|
||||
<button className={styles.playPauseButton} onClick={togglePlayPause}>
|
||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||
</button>
|
||||
{!isSmallSize && (<span className={styles.time}>{formatTime(currentTime)} / {formatTime(duration)}</span>)}
|
||||
</div>
|
||||
<div className={styles.rightControls}>
|
||||
<button className={styles.muteButton} onClick={toggleMute}>
|
||||
{isMuted ? <UnmuteIcon /> : <MuteIcon />}
|
||||
</button>
|
||||
{!isSmallSize && (
|
||||
<div className={styles.volumeControl}>
|
||||
<div
|
||||
ref={volumeRef}
|
||||
className={styles.volumeSlider}
|
||||
onClick={handleVolumeChange}
|
||||
onMouseDown={(e) => {
|
||||
handleVolumeChange(e)
|
||||
const handleMouseMove = (e: MouseEvent) => handleVolumeChange(e as unknown as React.MouseEvent<HTMLDivElement>)
|
||||
const handleMouseUp = () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
}}
|
||||
>
|
||||
<div className={styles.volumeLevel} style={{ width: `${volume * 100}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button className={styles.fullscreenButton} onClick={toggleFullscreen}>
|
||||
<FullscreenIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoPlayer
|
12
web/app/components/base/video-gallery/index.tsx
Normal file
12
web/app/components/base/video-gallery/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react'
|
||||
import VideoPlayer from './VideoPlayer'
|
||||
|
||||
type Props = {
|
||||
srcs: string[]
|
||||
}
|
||||
|
||||
const VideoGallery: React.FC<Props> = ({ srcs }) => {
|
||||
return (<><br/>{srcs.map((src, index) => (<><br/><VideoPlayer key={`video_${index}`} src={src}/></>))}</>)
|
||||
}
|
||||
|
||||
export default React.memo(VideoGallery)
|
@ -16,11 +16,13 @@ import { audioToText } from '@/service/share'
|
||||
type VoiceInputTypes = {
|
||||
onConverted: (text: string) => void
|
||||
onCancel: () => void
|
||||
wordTimestamps?: string
|
||||
}
|
||||
|
||||
const VoiceInput = ({
|
||||
onCancel,
|
||||
onConverted,
|
||||
wordTimestamps,
|
||||
}: VoiceInputTypes) => {
|
||||
const { t } = useTranslation()
|
||||
const recorder = useRef(new Recorder({
|
||||
@ -88,6 +90,7 @@ const VoiceInput = ({
|
||||
const mp3File = new File([mp3Blob], 'temp.mp3', { type: 'audio/mp3' })
|
||||
const formData = new FormData()
|
||||
formData.append('file', mp3File)
|
||||
formData.append('word_timestamps', wordTimestamps || 'disabled')
|
||||
|
||||
let url = ''
|
||||
let isPublic = false
|
||||
@ -112,7 +115,7 @@ const VoiceInput = ({
|
||||
onConverted('')
|
||||
onCancel()
|
||||
}
|
||||
}, [])
|
||||
}, [clearInterval, onCancel, onConverted, params.appId, params.token, pathname, wordTimestamps])
|
||||
const handleStartRecord = async () => {
|
||||
try {
|
||||
await recorder.current.start()
|
||||
@ -146,7 +149,7 @@ const VoiceInput = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
if (originDuration >= 120 && startRecord)
|
||||
if (originDuration >= 600 && startRecord)
|
||||
handleStopRecorder()
|
||||
|
||||
useEffect(() => {
|
||||
@ -204,7 +207,7 @@ const VoiceInput = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className={`w-[45px] pl-1 text-xs font-medium ${originDuration > 110 ? 'text-[#F04438]' : 'text-gray-700'}`}>{`0${minutes.toFixed(0)}:${seconds >= 10 ? seconds : `0${seconds}`}`}</div>
|
||||
<div className={`w-[45px] pl-1 text-xs font-medium ${originDuration > 500 ? 'text-[#F04438]' : 'text-gray-700'}`}>{`0${minutes.toFixed(0)}:${seconds >= 10 ? seconds : `0${seconds}`}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -37,6 +37,7 @@ const translation = {
|
||||
params: 'Params',
|
||||
duplicate: 'Duplicate',
|
||||
rename: 'Rename',
|
||||
audioSourceUnavailable: 'AudioSource is unavailable',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}} is required',
|
||||
|
@ -37,6 +37,7 @@ const translation = {
|
||||
params: '参数设置',
|
||||
duplicate: '复制',
|
||||
rename: '重命名',
|
||||
audioSourceUnavailable: '音源不可用',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}} 为必填项',
|
||||
|
@ -87,6 +87,7 @@
|
||||
"reactflow": "^11.11.3",
|
||||
"recordrtc": "^5.6.2",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-breaks": "^3.0.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
|
334
web/yarn.lock
334
web/yarn.lock
@ -260,6 +260,13 @@
|
||||
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz"
|
||||
integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==
|
||||
|
||||
"@babel/parser@^7.25.4":
|
||||
version "7.25.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f"
|
||||
integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.6"
|
||||
|
||||
"@babel/plugin-syntax-async-generators@^7.8.4":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
|
||||
@ -406,10 +413,10 @@
|
||||
"@babel/helper-validator-identifier" "^7.24.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.24.0":
|
||||
version "7.25.2"
|
||||
resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125"
|
||||
integrity sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==
|
||||
"@babel/types@^7.25.4", "@babel/types@^7.25.6":
|
||||
version "7.25.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6"
|
||||
integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.24.8"
|
||||
"@babel/helper-validator-identifier" "^7.24.7"
|
||||
@ -1465,7 +1472,7 @@
|
||||
|
||||
"@sindresorhus/is@^4.0.0":
|
||||
version "4.6.0"
|
||||
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
|
||||
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
|
||||
|
||||
"@sinonjs/commons@^3.0.0":
|
||||
@ -1497,7 +1504,7 @@
|
||||
|
||||
"@szmarczak/http-timer@^4.0.5":
|
||||
version "4.0.6"
|
||||
resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
|
||||
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
|
||||
integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
|
||||
dependencies:
|
||||
defer-to-connect "^2.0.0"
|
||||
@ -1624,7 +1631,7 @@
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
|
||||
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
|
||||
integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
|
||||
dependencies:
|
||||
"@types/http-cache-semantics" "*"
|
||||
@ -1890,9 +1897,16 @@
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/hast@^3.0.0":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa"
|
||||
integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/http-cache-semantics@*":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
|
||||
integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
||||
@ -1963,7 +1977,7 @@
|
||||
|
||||
"@types/keyv@^3.1.4":
|
||||
version "3.1.4"
|
||||
resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
|
||||
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
|
||||
integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
@ -1987,6 +2001,13 @@
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/mdast@^4.0.0":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6"
|
||||
integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/mdx@^2.0.0":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.5.tgz"
|
||||
@ -2080,7 +2101,7 @@
|
||||
|
||||
"@types/responselike@^1.0.0":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
|
||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
|
||||
integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
@ -2110,6 +2131,11 @@
|
||||
resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz"
|
||||
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
||||
|
||||
"@types/unist@^3.0.0":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20"
|
||||
integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==
|
||||
|
||||
"@types/uuid@^9.0.8":
|
||||
version "9.0.8"
|
||||
resolved "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz"
|
||||
@ -2211,6 +2237,11 @@
|
||||
"@typescript-eslint/types" "5.59.9"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@ungap/structured-clone@^1.0.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||
|
||||
"@vue/compiler-core@3.4.25":
|
||||
version "3.4.25"
|
||||
resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.25.tgz"
|
||||
@ -2630,7 +2661,7 @@ binary-extensions@^2.0.0:
|
||||
|
||||
bing-translate-api@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-4.0.2.tgz#52807a128e883bf074b4174c5e674ffca60685e7"
|
||||
resolved "https://registry.yarnpkg.com/bing-translate-api/-/bing-translate-api-4.0.2.tgz#52807a128e883bf074b4174c5e674ffca60685e7"
|
||||
integrity sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==
|
||||
dependencies:
|
||||
got "^11.8.6"
|
||||
@ -2729,12 +2760,12 @@ busboy@1.6.0:
|
||||
|
||||
cacheable-lookup@^5.0.3:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
|
||||
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
|
||||
|
||||
cacheable-request@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
|
||||
integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==
|
||||
dependencies:
|
||||
clone-response "^1.0.2"
|
||||
@ -2970,7 +3001,7 @@ cliui@^8.0.1:
|
||||
|
||||
clone-response@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
|
||||
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
|
||||
integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
@ -3548,7 +3579,7 @@ decode-named-character-reference@^1.0.0:
|
||||
|
||||
decompress-response@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
|
||||
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
|
||||
dependencies:
|
||||
mimic-response "^3.1.0"
|
||||
@ -3612,7 +3643,7 @@ default-browser@^4.0.0:
|
||||
|
||||
defer-to-connect@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
|
||||
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
|
||||
integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
|
||||
|
||||
define-data-property@^1.0.1, define-data-property@^1.1.1:
|
||||
@ -3665,6 +3696,13 @@ detect-newline@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||
|
||||
devlop@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018"
|
||||
integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==
|
||||
dependencies:
|
||||
dequal "^2.0.0"
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz"
|
||||
@ -3816,7 +3854,7 @@ emoji-regex@^9.2.2:
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
@ -4660,7 +4698,7 @@ get-package-type@^0.1.0:
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
@ -4785,7 +4823,7 @@ gopd@^1.0.1:
|
||||
|
||||
got@^11.8.6:
|
||||
version "11.8.6"
|
||||
resolved "https://registry.npmjs.org/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
|
||||
integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^4.0.0"
|
||||
@ -4905,6 +4943,20 @@ hast-util-from-parse5@^7.0.0:
|
||||
vfile-location "^4.0.0"
|
||||
web-namespaces "^2.0.0"
|
||||
|
||||
hast-util-from-parse5@^8.0.0:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz#654a5676a41211e14ee80d1b1758c399a0327651"
|
||||
integrity sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
"@types/unist" "^3.0.0"
|
||||
devlop "^1.0.0"
|
||||
hastscript "^8.0.0"
|
||||
property-information "^6.0.0"
|
||||
vfile "^6.0.0"
|
||||
vfile-location "^5.0.0"
|
||||
web-namespaces "^2.0.0"
|
||||
|
||||
hast-util-is-element@^2.0.0:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz"
|
||||
@ -4925,6 +4977,32 @@ hast-util-parse-selector@^3.0.0:
|
||||
dependencies:
|
||||
"@types/hast" "^2.0.0"
|
||||
|
||||
hast-util-parse-selector@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27"
|
||||
integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
|
||||
hast-util-raw@^9.0.0:
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-9.0.4.tgz#2da03e37c46eb1a6f1391f02f9b84ae65818f7ed"
|
||||
integrity sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
"@types/unist" "^3.0.0"
|
||||
"@ungap/structured-clone" "^1.0.0"
|
||||
hast-util-from-parse5 "^8.0.0"
|
||||
hast-util-to-parse5 "^8.0.0"
|
||||
html-void-elements "^3.0.0"
|
||||
mdast-util-to-hast "^13.0.0"
|
||||
parse5 "^7.0.0"
|
||||
unist-util-position "^5.0.0"
|
||||
unist-util-visit "^5.0.0"
|
||||
vfile "^6.0.0"
|
||||
web-namespaces "^2.0.0"
|
||||
zwitch "^2.0.0"
|
||||
|
||||
hast-util-to-estree@^2.0.0:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz"
|
||||
@ -4946,6 +5024,19 @@ hast-util-to-estree@^2.0.0:
|
||||
unist-util-position "^4.0.0"
|
||||
zwitch "^2.0.0"
|
||||
|
||||
hast-util-to-parse5@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed"
|
||||
integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
comma-separated-tokens "^2.0.0"
|
||||
devlop "^1.0.0"
|
||||
property-information "^6.0.0"
|
||||
space-separated-tokens "^2.0.0"
|
||||
web-namespaces "^2.0.0"
|
||||
zwitch "^2.0.0"
|
||||
|
||||
hast-util-to-text@^3.1.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz"
|
||||
@ -4983,6 +5074,17 @@ hastscript@^7.0.0:
|
||||
property-information "^6.0.0"
|
||||
space-separated-tokens "^2.0.0"
|
||||
|
||||
hastscript@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a"
|
||||
integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
comma-separated-tokens "^2.0.0"
|
||||
hast-util-parse-selector "^4.0.0"
|
||||
property-information "^6.0.0"
|
||||
space-separated-tokens "^2.0.0"
|
||||
|
||||
heap@^0.2.6:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz"
|
||||
@ -5024,6 +5126,11 @@ html-parse-stringify@^3.0.1:
|
||||
dependencies:
|
||||
void-elements "3.1.0"
|
||||
|
||||
html-void-elements@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7"
|
||||
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
|
||||
|
||||
htmlparser2@^8.0.1:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz"
|
||||
@ -5036,7 +5143,7 @@ htmlparser2@^8.0.1:
|
||||
|
||||
http-cache-semantics@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||
|
||||
http-proxy-agent@^5.0.0:
|
||||
@ -5050,7 +5157,7 @@ http-proxy-agent@^5.0.0:
|
||||
|
||||
http2-wrapper@^1.0.0-beta.5.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
|
||||
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
|
||||
integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
|
||||
dependencies:
|
||||
quick-lru "^5.1.1"
|
||||
@ -6045,7 +6152,7 @@ jsesc@~0.5.0:
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
|
||||
|
||||
json-parse-even-better-errors@^2.3.0:
|
||||
@ -6102,7 +6209,7 @@ katex@^0.16.0, katex@^0.16.10:
|
||||
|
||||
keyv@^4.0.0:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
@ -6280,7 +6387,7 @@ loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
|
||||
lowercase-keys@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lowlight@^1.17.0:
|
||||
@ -6316,12 +6423,12 @@ lz-string@^1.5.0:
|
||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||
|
||||
magicast@^0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz#bbda1791d03190a24b00ff3dd18151e7fd381d19"
|
||||
integrity sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739"
|
||||
integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.24.4"
|
||||
"@babel/types" "^7.24.0"
|
||||
"@babel/parser" "^7.25.4"
|
||||
"@babel/types" "^7.25.4"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
make-dir@^4.0.0:
|
||||
@ -6549,6 +6656,21 @@ mdast-util-to-hast@^12.1.0:
|
||||
unist-util-position "^4.0.0"
|
||||
unist-util-visit "^4.0.0"
|
||||
|
||||
mdast-util-to-hast@^13.0.0:
|
||||
version "13.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4"
|
||||
integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
"@types/mdast" "^4.0.0"
|
||||
"@ungap/structured-clone" "^1.0.0"
|
||||
devlop "^1.0.0"
|
||||
micromark-util-sanitize-uri "^2.0.0"
|
||||
trim-lines "^3.0.0"
|
||||
unist-util-position "^5.0.0"
|
||||
unist-util-visit "^5.0.0"
|
||||
vfile "^6.0.0"
|
||||
|
||||
mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz"
|
||||
@ -6865,6 +6987,14 @@ micromark-util-character@^1.0.0:
|
||||
micromark-util-symbol "^1.0.0"
|
||||
micromark-util-types "^1.0.0"
|
||||
|
||||
micromark-util-character@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1"
|
||||
integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==
|
||||
dependencies:
|
||||
micromark-util-symbol "^2.0.0"
|
||||
micromark-util-types "^2.0.0"
|
||||
|
||||
micromark-util-chunked@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz"
|
||||
@ -6911,6 +7041,11 @@ micromark-util-encode@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz"
|
||||
integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==
|
||||
|
||||
micromark-util-encode@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1"
|
||||
integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==
|
||||
|
||||
micromark-util-events-to-acorn@^1.0.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz"
|
||||
@ -6953,6 +7088,15 @@ micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0:
|
||||
micromark-util-encode "^1.0.0"
|
||||
micromark-util-symbol "^1.0.0"
|
||||
|
||||
micromark-util-sanitize-uri@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de"
|
||||
integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==
|
||||
dependencies:
|
||||
micromark-util-character "^2.0.0"
|
||||
micromark-util-encode "^2.0.0"
|
||||
micromark-util-symbol "^2.0.0"
|
||||
|
||||
micromark-util-subtokenize@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz"
|
||||
@ -6968,11 +7112,21 @@ micromark-util-symbol@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz"
|
||||
integrity sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==
|
||||
|
||||
micromark-util-symbol@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044"
|
||||
integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==
|
||||
|
||||
micromark-util-types@^1.0.0, micromark-util-types@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz"
|
||||
integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==
|
||||
|
||||
micromark-util-types@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e"
|
||||
integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==
|
||||
|
||||
micromark@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz"
|
||||
@ -7036,12 +7190,12 @@ mimic-fn@^4.0.0:
|
||||
|
||||
mimic-response@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
|
||||
|
||||
min-indent@^1.0.0:
|
||||
@ -7186,8 +7340,9 @@ normalize-range@^0.1.2:
|
||||
|
||||
normalize-url@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
normalize-wheel@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
|
||||
@ -7309,7 +7464,7 @@ object.values@^1.1.6, object.values@^1.1.7:
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
@ -7352,7 +7507,7 @@ optionator@^0.9.1:
|
||||
|
||||
p-cancelable@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
||||
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
|
||||
|
||||
p-limit@^2.2.0:
|
||||
@ -7683,7 +7838,7 @@ psl@^1.1.33:
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
@ -7728,7 +7883,7 @@ queue-microtask@^1.2.2:
|
||||
|
||||
quick-lru@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
|
||||
|
||||
rc-input@~1.3.5:
|
||||
@ -8063,6 +8218,15 @@ rehype-katex@^6.0.2:
|
||||
katex "^0.16.0"
|
||||
unist-util-visit "^4.0.0"
|
||||
|
||||
rehype-raw@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4"
|
||||
integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==
|
||||
dependencies:
|
||||
"@types/hast" "^3.0.0"
|
||||
hast-util-raw "^9.0.0"
|
||||
vfile "^6.0.0"
|
||||
|
||||
remark-breaks@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmjs.org/remark-breaks/-/remark-breaks-3.0.3.tgz"
|
||||
@ -8136,7 +8300,7 @@ resize-observer-polyfill@^1.5.1:
|
||||
|
||||
resolve-alpn@^1.0.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
@ -8186,7 +8350,7 @@ resolve@^2.0.0-next.4:
|
||||
|
||||
responselike@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
|
||||
resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
|
||||
integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==
|
||||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
@ -8590,7 +8754,7 @@ string-length@^4.0.1:
|
||||
|
||||
string-width@4.2.3, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
@ -8647,14 +8811,7 @@ stringify-entities@^4.0.0:
|
||||
character-entities-html4 "^2.0.0"
|
||||
character-entities-legacy "^3.0.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -8968,9 +9125,9 @@ tslib@^1.8.1, tslib@^1.9.3:
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.1:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
|
||||
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
|
||||
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
|
||||
|
||||
tslib@^2.1.0, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0:
|
||||
version "2.5.3"
|
||||
@ -9108,6 +9265,13 @@ unist-util-is@^5.0.0:
|
||||
dependencies:
|
||||
"@types/unist" "^2.0.0"
|
||||
|
||||
unist-util-is@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424"
|
||||
integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
|
||||
unist-util-position-from-estree@^1.0.0, unist-util-position-from-estree@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz"
|
||||
@ -9122,6 +9286,13 @@ unist-util-position@^4.0.0:
|
||||
dependencies:
|
||||
"@types/unist" "^2.0.0"
|
||||
|
||||
unist-util-position@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4"
|
||||
integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
|
||||
unist-util-remove-position@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz"
|
||||
@ -9144,6 +9315,13 @@ unist-util-stringify-position@^3.0.0:
|
||||
dependencies:
|
||||
"@types/unist" "^2.0.0"
|
||||
|
||||
unist-util-stringify-position@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2"
|
||||
integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
|
||||
unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz"
|
||||
@ -9152,6 +9330,14 @@ unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1:
|
||||
"@types/unist" "^2.0.0"
|
||||
unist-util-is "^5.0.0"
|
||||
|
||||
unist-util-visit-parents@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815"
|
||||
integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
unist-util-is "^6.0.0"
|
||||
|
||||
unist-util-visit@^4.0.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz"
|
||||
@ -9161,6 +9347,15 @@ unist-util-visit@^4.0.0:
|
||||
unist-util-is "^5.0.0"
|
||||
unist-util-visit-parents "^5.1.1"
|
||||
|
||||
unist-util-visit@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6"
|
||||
integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
unist-util-is "^6.0.0"
|
||||
unist-util-visit-parents "^6.0.0"
|
||||
|
||||
universalify@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
||||
@ -9267,6 +9462,14 @@ vfile-location@^4.0.0:
|
||||
"@types/unist" "^2.0.0"
|
||||
vfile "^5.0.0"
|
||||
|
||||
vfile-location@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3"
|
||||
integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
vfile "^6.0.0"
|
||||
|
||||
vfile-message@^3.0.0:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz"
|
||||
@ -9275,6 +9478,14 @@ vfile-message@^3.0.0:
|
||||
"@types/unist" "^2.0.0"
|
||||
unist-util-stringify-position "^3.0.0"
|
||||
|
||||
vfile-message@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181"
|
||||
integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
unist-util-stringify-position "^4.0.0"
|
||||
|
||||
vfile@^5.0.0:
|
||||
version "5.3.7"
|
||||
resolved "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz"
|
||||
@ -9285,6 +9496,15 @@ vfile@^5.0.0:
|
||||
unist-util-stringify-position "^3.0.0"
|
||||
vfile-message "^3.0.0"
|
||||
|
||||
vfile@^6.0.0:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.2.tgz#ef49548ea3d270097a67011921411130ceae7deb"
|
||||
integrity sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==
|
||||
dependencies:
|
||||
"@types/unist" "^3.0.0"
|
||||
unist-util-stringify-position "^4.0.0"
|
||||
vfile-message "^4.0.0"
|
||||
|
||||
vite-code-inspector-plugin@0.13.0:
|
||||
version "0.13.0"
|
||||
resolved "https://registry.npmjs.org/vite-code-inspector-plugin/-/vite-code-inspector-plugin-0.13.0.tgz"
|
||||
@ -9428,7 +9648,8 @@ word-wrap@^1.2.3:
|
||||
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz"
|
||||
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
name wrap-ansi-cjs
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@ -9446,15 +9667,6 @@ wrap-ansi@^6.2.0:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"
|
||||
@ -9581,4 +9793,4 @@ zustand@^4.4.1, zustand@^4.5.2:
|
||||
zwitch@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
|
||||
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==
|
||||
integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==
|
||||
|
Loading…
x
Reference in New Issue
Block a user