Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
72492e6c
提交
72492e6c
authored
8月 14, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refactor: 重构数字人模块
上级
5452e724
显示空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
3943 行增加
和
693 行删除
+3943
-693
package.json
apps/admin/package.json
+9
-9
xiling.js
apps/admin/src/components/editor/api/xiling.js
+42
-1
digitalHumanRequest.js
.../admin/src/components/editor/hooks/digitalHumanRequest.js
+228
-0
digitalHumanTaskStore.js
...dmin/src/components/editor/hooks/digitalHumanTaskStore.js
+39
-0
useDigitalHuman.js
apps/admin/src/components/editor/hooks/useDigitalHuman.js
+279
-85
digital-human-request.test.ts
...ngeditor-customer/__tests__/digital-human-request.test.ts
+212
-0
digital-human-task-store.test.ts
...ditor-customer/__tests__/digital-human-task-store.test.ts
+60
-0
DigitalHumanBaiduUploadField.jsx
...stomer/features/ai-media/DigitalHumanBaiduUploadField.jsx
+129
-0
DigitalHumanCustomizePanel.jsx
...customer/features/ai-media/DigitalHumanCustomizePanel.jsx
+157
-0
DigitalHumanDialog.jsx
...geditor-customer/features/ai-media/DigitalHumanDialog.jsx
+1105
-167
DigitalHumanUploadField.jsx
...or-customer/features/ai-media/DigitalHumanUploadField.jsx
+165
-0
digital-human.less
.../wangeditor-customer/features/ai-media/digital-human.less
+1262
-129
package.json
apps/learning/package.json
+4
-4
pnpm-lock.yaml
pnpm-lock.yaml
+252
-298
没有找到文件。
apps/admin/package.json
浏览文件 @
72492e6c
...
@@ -28,20 +28,20 @@
...
@@ -28,20 +28,20 @@
"axios"
:
"^1.18.1"
,
"axios"
:
"^1.18.1"
,
"dayjs"
:
"^1.11.11"
,
"dayjs"
:
"^1.11.11"
,
"easy-formula-editor"
:
"^0.0.2-alpha.1"
,
"easy-formula-editor"
:
"^0.0.2-alpha.1"
,
"echarts"
:
"^
5.4.3
"
,
"echarts"
:
"^
6.1.0
"
,
"file-saver"
:
"^2.0.5"
,
"file-saver"
:
"^2.0.5"
,
"highlight.js"
:
"^11.11.1"
,
"highlight.js"
:
"^11.11.1"
,
"jquery"
:
"^3.7.1"
,
"jquery"
:
"^3.7.1"
,
"js-cookie"
:
"^3.0.
5
"
,
"js-cookie"
:
"^3.0.
8
"
,
"js-md5"
:
"^0.8.3"
,
"js-md5"
:
"^0.8.3"
,
"lodash-es"
:
"^4.1
7.2
1"
,
"lodash-es"
:
"^4.1
8.
1"
,
"markdown-it"
:
"^14.
1
.0"
,
"markdown-it"
:
"^14.
3
.0"
,
"mathjax"
:
"3.2.2"
,
"mathjax"
:
"3.2.2"
,
"nanoid"
:
"^3.3.8"
,
"nanoid"
:
"^3.3.
1
8"
,
"react"
:
"^18.3.1"
,
"react"
:
"^18.3.1"
,
"react-dom"
:
"^18.3.1"
,
"react-dom"
:
"^18.3.1"
,
"react-error-boundary"
:
"^4.0.13"
,
"react-error-boundary"
:
"^4.0.13"
,
"react-router"
:
"^7.18.
1
"
,
"react-router"
:
"^7.18.
2
"
,
"react-use"
:
"^17.6.0"
,
"react-use"
:
"^17.6.0"
,
"snabbdom"
:
"^3.6.2"
,
"snabbdom"
:
"^3.6.2"
,
"zustand"
:
"^4.5.5"
"zustand"
:
"^4.5.5"
...
@@ -52,14 +52,14 @@
...
@@ -52,14 +52,14 @@
"@testing-library/user-event"
:
"^14.6.1"
,
"@testing-library/user-event"
:
"^14.6.1"
,
"@types/react"
:
"^18.3.7"
,
"@types/react"
:
"^18.3.7"
,
"@types/react-dom"
:
"^18.3.0"
,
"@types/react-dom"
:
"^18.3.0"
,
"@vitejs/plugin-react"
:
"^6.0.
3
"
,
"@vitejs/plugin-react"
:
"^6.0.
5
"
,
"jsdom"
:
"^29.1.1"
,
"jsdom"
:
"^29.1.1"
,
"less"
:
"^4.2.0"
,
"less"
:
"^4.2.0"
,
"oxfmt"
:
"^0.59.0"
,
"oxfmt"
:
"^0.59.0"
,
"oxlint"
:
"^1.74.0"
,
"oxlint"
:
"^1.74.0"
,
"typescript"
:
"^7.0.2"
,
"typescript"
:
"^7.0.2"
,
"vite"
:
"^8.
1.5
"
,
"vite"
:
"^8.
2.1
"
,
"vite-plugin-mkcert"
:
"^
1.17.1
0"
,
"vite-plugin-mkcert"
:
"^
2.1.
0"
,
"vitest"
:
"^4.1.10"
"vitest"
:
"^4.1.10"
},
},
"browserslist"
:
[
"browserslist"
:
[
...
...
apps/admin/src/components/editor/api/xiling.js
浏览文件 @
72492e6c
...
@@ -72,9 +72,20 @@ export const upload = async (data, options = {}) => {
...
@@ -72,9 +72,20 @@ export const upload = async (data, options = {}) => {
return
response
.
result
return
response
.
result
}
}
export
const
uploadDigitalHumanFile
=
async
(
file
,
providerType
,
options
=
{})
=>
{
const
data
=
new
FormData
()
data
.
append
(
'file'
,
file
)
const
response
=
await
request
.
post
(
'/api/digitalhuman/open/v1/file/upload'
,
data
,
{
params
:
{
providerType
,
sourceFileName
:
file
.
name
},
headers
:
{
'Content-Type'
:
'multipart/form-data'
},
...
options
,
})
return
response
.
result
}
// 获取数字人列表
// 获取数字人列表
export
const
getDigitalHumanList
=
async
(
params
,
options
)
=>
{
export
const
getDigitalHumanList
=
async
(
params
,
options
)
=>
{
const
{
systemFigure
,
item
=
'VIDEO-DH_2D'
,
pageSize
=
1000
}
=
params
const
{
systemFigure
,
item
=
'VIDEO-DH_2D
_LITE
'
,
pageSize
=
1000
}
=
params
const
response
=
await
request
.
get
(
'/api/digitalhuman/open/v1/figure/query'
,
{
const
response
=
await
request
.
get
(
'/api/digitalhuman/open/v1/figure/query'
,
{
params
:
{
systemFigure
,
item
,
pageSize
},
params
:
{
systemFigure
,
item
,
pageSize
},
...
options
,
...
options
,
...
@@ -82,6 +93,15 @@ export const getDigitalHumanList = async (params, options) => {
...
@@ -82,6 +93,15 @@ export const getDigitalHumanList = async (params, options) => {
return
response
.
result
?.
result
||
[]
return
response
.
result
?.
result
||
[]
}
}
// 获取系统音色列表
export
const
getDigitalHumanVoiceList
=
async
(
params
=
{},
options
)
=>
{
const
response
=
await
request
.
get
(
'/api/digitalhuman/open/v1/tts/persons'
,
{
params
:
{
isSystem
:
true
,
...
params
},
...
options
,
})
return
response
.
result
?.
result
||
response
.
result
||
[]
}
/**
/**
* 提交生成式数字人形象定制任务
* 提交生成式数字人形象定制任务
*/
*/
...
@@ -130,3 +150,24 @@ export const submitVideoAdvanced = async (params, options = {}) => {
...
@@ -130,3 +150,24 @@ export const submitVideoAdvanced = async (params, options = {}) => {
export
const
getVideoAdvancedTask
=
async
(
params
,
options
=
{})
=>
{
export
const
getVideoAdvancedTask
=
async
(
params
,
options
=
{})
=>
{
return
request
.
get
(
'/api/digitalhuman/open/v1/video/advanced/task'
,
{
params
,
...
options
})
return
request
.
get
(
'/api/digitalhuman/open/v1/video/advanced/task'
,
{
params
,
...
options
})
}
}
/**
* 校验照片数字人人像图片
*/
export
const
verifyImageVideo
=
async
(
params
,
options
=
{})
=>
{
return
request
.
post
(
'/api/digitalhuman/open/v1/video/image/verify'
,
params
,
options
)
}
/**
* 提交照片数字人视频任务
*/
export
const
submitImageVideo
=
async
(
params
,
options
=
{})
=>
{
return
request
.
post
(
'/api/digitalhuman/open/v1/video/image/submit'
,
params
,
options
)
}
/**
* 查询照片数字人视频任务
*/
export
const
getImageVideoTask
=
async
(
params
,
options
=
{})
=>
{
return
request
.
get
(
'/api/digitalhuman/open/v1/video/image/task'
,
{
params
,
...
options
})
}
apps/admin/src/components/editor/hooks/digitalHumanRequest.js
0 → 100644
浏览文件 @
72492e6c
export
const
DIGITAL_HUMAN_TYPES
=
{
BASIC
:
'basic'
,
ADVANCED
:
'advanced'
,
FAST
:
'fast'
,
PHOTO
:
'photo'
,
}
export
const
DIGITAL_HUMAN_TYPE_LABELS
=
{
[
DIGITAL_HUMAN_TYPES
.
BASIC
]:
'基础接口'
,
[
DIGITAL_HUMAN_TYPES
.
ADVANCED
]:
'高级接口'
,
[
DIGITAL_HUMAN_TYPES
.
FAST
]:
'已有真人视频'
,
[
DIGITAL_HUMAN_TYPES
.
PHOTO
]:
'照片数字人'
,
}
// 视频组件只在这里切换接口,页面不再把接口能力暴露成四个并列选项。
export
const
DIGITAL_HUMAN_VIDEO_CONFIG
=
{
interfaceType
:
DIGITAL_HUMAN_TYPES
.
BASIC
,
figureItem
:
'VIDEO-DH_2D_LITE'
,
}
export
function
getConfiguredDigitalHumanType
()
{
return
DIGITAL_HUMAN_VIDEO_CONFIG
.
interfaceType
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
DIGITAL_HUMAN_TYPES
.
ADVANCED
:
DIGITAL_HUMAN_TYPES
.
BASIC
}
export
const
ADVANCED_TEMPLATES
=
[
{
value
:
't-pf4kqasspwzwyexyte121'
,
label
:
'视频样式 1'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/1_8c5a2e7.png'
,
},
{
value
:
't-af4keqsspfzwyexyte123'
,
label
:
'视频样式 2'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/2_755cd08.png'
,
},
{
value
:
't-ad4eeqsspfzwyqxyte125'
,
label
:
'视频样式 3'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/3_c730e57.png'
,
},
{
value
:
't-cd4eeqsspfzwyqxyte127'
,
label
:
'视频样式 4'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/4_d542bdf.png'
,
},
{
value
:
't-hb4eeqsspfzwyqxyte129'
,
label
:
'视频样式 5'
,
ratio
:
'landscape'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/5_5708421.png'
,
},
{
value
:
't-cd4eeqsspfzwyqxyteditu3'
,
label
:
'视频样式 6'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%20%284%29_a9ef435.png'
,
},
{
value
:
't-cd4eeqsspfzwyqxyteditu4'
,
label
:
'视频样式 7'
,
ratio
:
'portrait'
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%20%285%29_4f18cab.png'
,
},
{
value
:
't-cd4eeqsspfzwyqxyteditu5'
,
label
:
'视频样式 8'
,
ratio
:
'portrait'
,
withoutFigure
:
true
,
preview
:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/%E6%97%A0%E6%95%B0%E5%AD%97%E4%BA%BA%E6%A8%A1%E6%9D%BF_97739d1.png'
,
},
]
function
compact
(
value
)
{
if
(
Array
.
isArray
(
value
))
return
value
.
filter
(
Boolean
)
if
(
!
value
||
typeof
value
!==
'object'
)
return
value
return
Object
.
fromEntries
(
Object
.
entries
(
value
)
.
filter
(([,
item
])
=>
item
!==
''
&&
item
!==
undefined
&&
item
!==
null
)
.
map
(([
key
,
item
])
=>
[
key
,
compact
(
item
)]),
)
}
function
buildDriveParams
(
options
,
includeLanguage
=
true
)
{
if
(
options
.
driveType
===
'VOICE'
)
{
return
{
driveType
:
'VOICE'
,
inputAudioUrl
:
options
.
inputAudioUrl
,
}
}
return
{
driveType
:
'TEXT'
,
text
:
options
.
text
,
ttsParams
:
{
person
:
options
.
voiceId
,
lan
:
includeLanguage
?
options
.
language
||
undefined
:
undefined
,
speed
:
String
(
options
.
speed
??
5
),
volume
:
String
(
options
.
volume
??
5
),
pitch
:
String
(
options
.
pitch
??
5
),
},
}
}
export
function
buildDigitalHumanRequest
(
type
,
options
)
{
const
driveParams
=
buildDriveParams
(
options
,
type
!==
DIGITAL_HUMAN_TYPES
.
ADVANCED
)
if
(
type
===
DIGITAL_HUMAN_TYPES
.
FAST
)
{
return
compact
({
templateVideoId
:
options
.
templateVideoId
,
...
driveParams
,
})
}
if
(
type
===
DIGITAL_HUMAN_TYPES
.
PHOTO
)
{
return
compact
({
inputImageUrl
:
options
.
inputImageUrl
,
model
:
options
.
model
,
...
driveParams
,
subtitleParams
:
{
enabled
:
options
.
driveType
===
'TEXT'
&&
!!
options
.
enableSubtitle
,
subtitlePolicy
:
'SRT'
,
},
})
}
if
(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
)
{
return
compact
({
figureId
:
options
.
figureId
,
templateId
:
options
.
templateId
,
model
:
options
.
model
,
...
driveParams
,
title
:
options
.
title
,
logoParams
:
options
.
logoUrl
?
{
logoUrl
:
options
.
logoUrl
}
:
undefined
,
bgmParams
:
options
.
bgmUrl
?
{
bgmUrl
:
options
.
bgmUrl
}
:
undefined
,
materialUrl
:
options
.
materialUrl
,
extendBackgroundUrl
:
options
.
extendBackgroundUrl
,
riskTip
:
options
.
riskTip
,
openingMaterial
:
options
.
openingFileId
?
{
fileId
:
options
.
openingFileId
,
mediaType
:
'VIDEO'
}
:
options
.
openingUrl
?
{
fileUrl
:
options
.
openingUrl
,
mediaType
:
'VIDEO'
}
:
undefined
,
endingMaterial
:
options
.
endingFileId
?
{
fileId
:
options
.
endingFileId
,
mediaType
:
'VIDEO'
}
:
options
.
endingUrl
?
{
fileUrl
:
options
.
endingUrl
,
mediaType
:
'VIDEO'
}
:
undefined
,
mashupMaterials
:
options
.
mashupMaterials
?.
map
(({
fileId
,
fileUrl
,
mediaType
})
=>
({
fileId
,
fileUrl
,
mediaType
,
})),
videoParams
:
{
width
:
Number
(
options
.
width
),
height
:
Number
(
options
.
height
),
},
dhParams
:
options
.
dhParams
,
fissionParams
:
options
.
driveType
===
'TEXT'
&&
options
.
fissionFigureIds
?.
length
?
{
figureIds
:
options
.
fissionFigureIds
,
ttsPersons
:
options
.
fissionVoiceIds
,
}
:
undefined
,
})
}
return
compact
({
figureId
:
options
.
figureId
,
...
driveParams
,
backgroundImageUrl
:
options
.
transparent
?
undefined
:
options
.
backgroundImageUrl
,
playControlParams
:
options
.
enableSegmentLoop
?
{
controlType
:
'SEGMENT_LOOP'
,
startTimestamp
:
Number
(
options
.
startTimestamp
||
0
),
endTimestamp
:
Number
(
options
.
endTimestamp
),
}
:
undefined
,
videoParams
:
{
width
:
Number
(
options
.
width
),
height
:
Number
(
options
.
height
),
transparent
:
!!
options
.
transparent
,
},
dhParams
:
options
.
dhParams
||
(
options
.
positionMode
===
'camera'
?
{
cameraId
:
Number
(
options
.
cameraId
)
}
:
options
.
positionMode
===
'pixel'
?
{
positionV2
:
{
crop
:
{
top
:
Number
(
options
.
cropTop
),
left
:
Number
(
options
.
cropLeft
),
width
:
Number
(
options
.
cropWidth
),
height
:
Number
(
options
.
cropHeight
),
},
location
:
{
top
:
Number
(
options
.
locationTop
),
left
:
Number
(
options
.
locationLeft
),
width
:
Number
(
options
.
locationWidth
),
height
:
Number
(
options
.
locationHeight
),
},
},
}
:
options
.
positionX
!==
undefined
?
{
position
:
{
x
:
Number
(
options
.
positionX
),
y
:
Number
(
options
.
positionY
),
z
:
Number
(
options
.
positionZ
),
},
}
:
undefined
),
autoAnimoji
:
options
.
driveType
===
'TEXT'
&&
!!
options
.
autoAnimoji
,
subtitleParams
:
{
enabled
:
options
.
driveType
===
'TEXT'
&&
!!
options
.
enableSubtitle
,
subtitlePolicy
:
'SRT'
,
},
})
}
apps/admin/src/components/editor/hooks/digitalHumanTaskStore.js
0 → 100644
浏览文件 @
72492e6c
const
STORAGE_KEY
=
'center-book:digital-human-tasks'
const
MAX_TASKS
=
10
function
getStorage
(
storage
)
{
if
(
storage
)
return
storage
if
(
typeof
window
===
'undefined'
)
return
null
return
window
.
localStorage
}
export
function
readDigitalHumanTasks
(
storage
)
{
try
{
const
value
=
getStorage
(
storage
)?.
getItem
(
STORAGE_KEY
)
const
tasks
=
value
?
JSON
.
parse
(
value
)
:
[]
return
Array
.
isArray
(
tasks
)
?
tasks
:
[]
}
catch
{
return
[]
}
}
export
function
saveDigitalHumanTask
(
task
,
storage
)
{
const
target
=
getStorage
(
storage
)
if
(
!
target
||
!
task
?.
taskId
)
return
[]
const
tasks
=
readDigitalHumanTasks
(
target
)
const
nextTasks
=
[
task
,
...
tasks
.
filter
((
item
)
=>
item
.
taskId
!==
task
.
taskId
)].
slice
(
0
,
MAX_TASKS
,
)
try
{
target
.
setItem
(
STORAGE_KEY
,
JSON
.
stringify
(
nextTasks
))
}
catch
{
return
tasks
}
return
nextTasks
}
export
function
clearDigitalHumanTasks
(
storage
)
{
getStorage
(
storage
)?.
removeItem
(
STORAGE_KEY
)
}
apps/admin/src/components/editor/hooks/useDigitalHuman.js
浏览文件 @
72492e6c
import
{
useState
,
useEffect
,
useRef
,
useCallback
}
from
'react'
import
{
useState
,
useEffect
,
useRef
,
useCallback
}
from
'react'
import
{
message
}
from
'antd'
import
{
message
}
from
'antd'
import
{
getDigitalHumanList
,
submitVideo
,
getVideoTask
}
from
'@/components/editor/api/xiling'
import
{
getDigitalHumanList
,
getDigitalHumanVoiceList
,
getImageVideoTask
,
getVideoAdvancedTask
,
getVideoTask
,
submitImageVideo
,
submitVideo
,
submitVideoAdvanced
,
submitVideoFast
,
}
from
'@/components/editor/api/xiling'
import
{
import
{
digitalHumans
as
staticFigures
,
digitalHumans
as
staticFigures
,
femaleVoices
,
femaleVoices
as
staticFemaleVoices
,
maleVoices
,
maleVoices
as
staticMaleVoices
,
}
from
'@/components/editor/wangeditor-customer/public-api'
}
from
'@/components/editor/wangeditor-customer/public-api'
import
{
readDigitalHumanTasks
,
saveDigitalHumanTask
,
}
from
'@/components/editor/hooks/digitalHumanTaskStore'
import
{
buildDigitalHumanRequest
,
DIGITAL_HUMAN_VIDEO_CONFIG
,
DIGITAL_HUMAN_TYPES
,
}
from
'@/components/editor/hooks/digitalHumanRequest'
const
POLL_INTERVAL
=
5000
const
MAX_POLL_ERRORS
=
3
const
MAX_POLL_DURATION
=
30
*
60
*
1000
/**
function
normalizeDigitalHumanVoice
(
voice
)
{
* 数字人功能 Hook (简化版:仅文本驱动,移除底图)
const
gender
=
String
(
voice
.
gender
||
''
).
toLowerCase
()
*/
return
{
id
:
voice
.
perId
||
voice
.
id
,
name
:
voice
.
name
,
gender
:
gender
===
'male'
?
'男声'
:
gender
===
'female'
?
'女声'
:
'未知音色'
,
style
:
voice
.
describe
||
voice
.
style
||
''
,
previewUrl
:
voice
.
exampleAudioUrl
||
voice
.
previewUrl
,
version
:
voice
.
version
,
systemProvided
:
voice
.
systemProvided
,
}
}
function
queryVideoTask
(
task
)
{
const
params
=
{
taskId
:
task
.
taskId
}
if
(
task
.
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
)
return
getVideoAdvancedTask
(
params
)
if
(
task
.
type
===
DIGITAL_HUMAN_TYPES
.
PHOTO
)
return
getImageVideoTask
(
params
)
return
getVideoTask
(
params
)
}
function
submitVideoTask
(
type
,
params
)
{
if
(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
)
return
submitVideoAdvanced
(
params
)
if
(
type
===
DIGITAL_HUMAN_TYPES
.
FAST
)
return
submitVideoFast
(
params
)
if
(
type
===
DIGITAL_HUMAN_TYPES
.
PHOTO
)
return
submitImageVideo
(
params
)
return
submitVideo
(
params
)
}
/** 数字人视频任务:提交、轮询、恢复与重试。 */
export
default
function
useDigitalHuman
()
{
export
default
function
useDigitalHuman
()
{
const
[
figures
,
setFigures
]
=
useState
(
staticFigures
)
const
[
figures
,
setFigures
]
=
useState
([])
const
[
femaleVoices
,
setFemaleVoices
]
=
useState
([])
const
[
maleVoices
,
setMaleVoices
]
=
useState
([])
const
[
loadingFigures
,
setLoadingFigures
]
=
useState
(
false
)
const
[
loadingFigures
,
setLoadingFigures
]
=
useState
(
false
)
const
[
isGenerating
,
setIsGenerating
]
=
useState
(
false
)
const
[
isGenerating
,
setIsGenerating
]
=
useState
(
false
)
const
[
generatedVideo
,
setGeneratedVideo
]
=
useState
(
null
)
const
[
generatedVideo
,
setGeneratedVideo
]
=
useState
(
null
)
const
[
error
,
setError
]
=
useState
(
null
)
const
[
error
,
setError
]
=
useState
(
null
)
const
[
tasks
,
setTasks
]
=
useState
(
readDigitalHumanTasks
)
const
[
activeTask
,
setActiveTask
]
=
useState
(
null
)
const
pollTimerRef
=
useRef
(
null
)
const
pollTimerRef
=
useRef
(
null
)
const
pollingTaskIdRef
=
useRef
(
null
)
const
stopPolling
=
useCallback
(()
=>
{
const
stopPolling
=
useCallback
(()
=>
{
if
(
pollTimerRef
.
current
)
{
if
(
pollTimerRef
.
current
)
{
clear
Interval
(
pollTimerRef
.
current
)
clear
Timeout
(
pollTimerRef
.
current
)
pollTimerRef
.
current
=
null
pollTimerRef
.
current
=
null
}
}
pollingTaskIdRef
.
current
=
null
},
[])
const
persistTask
=
useCallback
((
task
)
=>
{
setTasks
(
saveDigitalHumanTask
(
task
))
setActiveTask
(
task
)
return
task
},
[])
},
[])
const
startPolling
=
useCallback
(
const
startPolling
=
useCallback
(
(
task
Id
,
figureName
)
=>
{
(
task
)
=>
{
stopPolling
()
stopPolling
()
pollTimerRef
.
current
=
setInterval
(
async
()
=>
{
pollingTaskIdRef
.
current
=
task
.
taskId
setIsGenerating
(
true
)
setError
(
null
)
let
pollErrors
=
0
const
startedAt
=
Date
.
now
()
const
poll
=
async
()
=>
{
if
(
pollingTaskIdRef
.
current
!==
task
.
taskId
)
return
try
{
try
{
const
response
=
await
getVideoTask
({
taskId
})
const
response
=
await
queryVideoTask
(
task
)
const
{
status
,
videoUrl
}
=
response
.
result
const
result
=
response
.
result
||
{}
const
{
status
,
videoUrl
}
=
result
pollErrors
=
0
if
(
status
===
'SUCCESS'
&&
videoUrl
)
{
if
(
status
===
'SUCCESS'
&&
videoUrl
)
{
stopPolling
()
stopPolling
()
setGeneratedVideo
({
const
completedTask
=
persistTask
({
...
task
,
...
result
,
status
:
'SUCCESS'
,
url
:
videoUrl
,
url
:
videoUrl
,
taskId
:
taskId
,
updatedAt
:
Date
.
now
(),
figureName
:
figureName
,
})
})
setGeneratedVideo
(
completedTask
)
setIsGenerating
(
false
)
setIsGenerating
(
false
)
message
.
success
(
'数字人视频生成成功
!
'
)
message
.
success
(
'数字人视频生成成功'
)
}
else
if
(
status
===
'FAILED'
)
{
}
else
if
(
status
===
'FAILED'
)
{
stopPolling
()
stopPolling
()
const
failedMessage
=
result
.
failedMessage
||
'生成失败'
persistTask
({
...
task
,
...
result
,
status
:
'FAILED'
,
error
:
failedMessage
,
updatedAt
:
Date
.
now
(),
})
setIsGenerating
(
false
)
setIsGenerating
(
false
)
setError
(
response
.
result
.
failedMessage
||
'生成失败'
)
setError
(
failedMessage
)
message
.
error
(
`视频生成失败:
${
response
.
result
.
failedMessage
||
'未知错误'
}
`
)
message
.
error
(
`视频生成失败:
${
failedMessage
}
`
)
}
else
if
(
Date
.
now
()
-
startedAt
>=
MAX_POLL_DURATION
)
{
stopPolling
()
const
timeoutMessage
=
'查询已暂停,稍后可继续查询任务状态'
persistTask
({
...
task
,
...
result
,
status
:
'PAUSED'
,
error
:
timeoutMessage
,
updatedAt
:
Date
.
now
(),
})
setIsGenerating
(
false
)
setError
(
timeoutMessage
)
}
else
{
const
processingTask
=
{
...
task
,
...
result
,
status
:
'PROCESSING'
,
updatedAt
:
Date
.
now
(),
}
persistTask
(
processingTask
)
pollTimerRef
.
current
=
setTimeout
(
poll
,
POLL_INTERVAL
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
console
.
error
(
'Polling Error:'
,
err
)
console
.
error
(
'Polling Error:'
,
err
)
pollErrors
+=
1
if
(
pollErrors
>=
MAX_POLL_ERRORS
)
{
stopPolling
()
const
networkMessage
=
'网络异常,已暂停查询,可稍后继续'
persistTask
({
...
task
,
status
:
'PAUSED'
,
error
:
networkMessage
,
updatedAt
:
Date
.
now
(),
})
setIsGenerating
(
false
)
setError
(
networkMessage
)
}
else
{
pollTimerRef
.
current
=
setTimeout
(
poll
,
POLL_INTERVAL
)
}
}
},
5000
)
}
}
poll
()
},
},
[
stopPolling
],
[
persistTask
,
stopPolling
],
)
)
//
处理获取列表
//
一次加载公共和已定制形象,弹框内仅负责滚动浏览。
const
fetchFigures
=
useCallback
(
async
()
=>
{
const
fetchFigures
=
useCallback
(
async
()
=>
{
setLoadingFigures
(
true
)
setLoadingFigures
(
true
)
try
{
try
{
const
list
=
await
getDigitalHumanList
({
systemFigure
:
true
})
const
list
=
await
getDigitalHumanList
({
if
(
list
&&
list
.
length
>
0
)
{
item
:
DIGITAL_HUMAN_VIDEO_CONFIG
.
figureItem
,
setFigures
((
prev
)
=>
{
pageSize
:
1000
,
// 创建新数组,保留静态配置,并用 API 的数据补充或更新关键信息(如 avatar)
})
const
combined
=
[...
prev
]
list
.
forEach
((
item
)
=>
{
const
existingIndex
=
combined
.
findIndex
((
c
)
=>
String
(
c
.
id
)
===
String
(
item
.
figureId
))
const
avatarUrl
=
item
.
figureImageUrl
||
item
.
figureVideoThumbnailUrl
if
(
existingIndex
>
-
1
)
{
if
(
list
&&
list
.
length
>
0
)
{
// 如果静态配置已存在,且 API 返回了有效的预览图,则更新它
setFigures
(
if
(
avatarUrl
)
{
list
.
map
((
item
)
=>
({
combined
[
existingIndex
]
=
{
...
combined
[
existingIndex
],
avatar
:
avatarUrl
,
}
}
}
else
{
// 如果是全新的数字人,则添加
combined
.
push
({
id
:
item
.
figureId
,
id
:
item
.
figureId
,
name
:
item
.
name
,
name
:
item
.
name
,
avatar
:
avatarUrl
,
avatar
:
item
.
figureImageUrl
||
item
.
figureVideoThumbnailUrl
||
item
.
templateImg
,
})
gender
:
item
.
gender
,
}
tags
:
item
.
tags
,
})
resolutionWidth
:
item
.
resolutionWidth
,
return
combined
resolutionHeight
:
item
.
resolutionHeight
,
})
pose
:
item
.
pose
,
background
:
item
.
background
,
age
:
item
.
age
,
templateVideoUrl
:
item
.
templateVideoUrl
,
})),
)
}
else
{
setFigures
(
staticFigures
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
console
.
error
(
'Fetch Figures Error:'
,
err
)
console
.
error
(
'Fetch Figures Error:'
,
err
)
setFigures
(
staticFigures
)
}
finally
{
}
finally
{
setLoadingFigures
(
false
)
setLoadingFigures
(
false
)
}
}
...
@@ -99,61 +215,89 @@ export default function useDigitalHuman() {
...
@@ -99,61 +215,89 @@ export default function useDigitalHuman() {
const
generateVideo
=
useCallback
(
const
generateVideo
=
useCallback
(
async
(
options
)
=>
{
async
(
options
)
=>
{
const
{
const
type
=
options
.
type
||
DIGITAL_HUMAN_TYPES
.
BASIC
figureId
,
figureName
,
text
,
voiceId
,
width
=
720
,
height
=
1280
,
transparent
=
true
,
enableSubtitle
=
false
,
backgroundImageUrl
,
}
=
options
setIsGenerating
(
true
)
setIsGenerating
(
true
)
setGeneratedVideo
(
null
)
setGeneratedVideo
(
null
)
setError
(
null
)
setError
(
null
)
try
{
try
{
const
params
=
{
const
params
=
buildDigitalHumanRequest
(
type
,
options
)
figureId
,
driveType
:
'TEXT'
,
text
,
...(
backgroundImageUrl
?
{
backgroundImageUrl
}
:
{}),
ttsParams
:
{
person
:
voiceId
||
'5116'
,
speed
:
'5'
,
pitch
:
'5'
,
volume
:
'5'
,
},
videoParams
:
{
width
:
parseInt
(
width
),
height
:
parseInt
(
height
),
transparent
:
!!
transparent
,
},
subtitleParams
:
{
enabled
:
!!
enableSubtitle
,
subtitlePolicy
:
'SRT'
,
},
}
const
response
=
await
submitVideo
(
params
)
const
response
=
await
submitVideoTask
(
type
,
params
)
if
(
response
.
result
&&
response
.
result
.
taskId
)
{
const
result
=
response
.
result
||
{}
startPolling
(
response
.
result
.
taskId
,
figureName
)
const
submittedTasks
=
result
.
taskId
return
response
.
result
.
taskId
?
[
result
]
:
(
result
.
fissionTasks
||
[]).
filter
((
item
)
=>
item
.
code
===
0
)
if
(
submittedTasks
.
length
>
0
)
{
const
createdAt
=
Date
.
now
()
const
tasks
=
submittedTasks
.
map
((
item
)
=>
persistTask
({
taskId
:
item
.
taskId
,
type
,
status
:
'PROCESSING'
,
figureName
:
options
.
figureName
||
item
.
figureId
||
'数字人'
,
createdAt
,
updatedAt
:
createdAt
,
options
:
{
...
options
,
figureId
:
item
.
figureId
||
options
.
figureId
,
voiceId
:
item
.
ttsPerson
||
options
.
voiceId
,
},
}),
)
startPolling
(
tasks
[
0
])
return
tasks
[
0
].
taskId
}
else
{
}
else
{
throw
new
Error
(
'
TaskId missing
'
)
throw
new
Error
(
'
未获取到任务编号,请稍后重试
'
)
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
setIsGenerating
(
false
)
setIsGenerating
(
false
)
const
msg
=
err
.
response
?.
data
?.
message
?.
global
||
'提交失败'
const
msg
=
err
.
response
?.
data
?.
message
?.
global
||
err
.
data
?.
message
?.
global
||
err
.
message
||
'提交失败'
setError
(
msg
)
setError
(
msg
)
message
.
error
(
msg
)
message
.
error
(
msg
)
throw
err
throw
err
}
}
},
},
[
startPolling
],
[
persistTask
,
startPolling
],
)
const
selectTask
=
useCallback
(
(
task
)
=>
{
if
(
task
.
status
===
'PROCESSING'
)
{
setGeneratedVideo
(
null
)
startPolling
(
task
)
return
}
stopPolling
()
setActiveTask
(
task
)
setError
(
task
.
error
||
null
)
setGeneratedVideo
(
task
.
status
===
'SUCCESS'
&&
task
.
url
?
task
:
null
)
setIsGenerating
(
false
)
},
[
startPolling
,
stopPolling
],
)
const
resumeTask
=
useCallback
(
(
task
=
activeTask
)
=>
{
if
(
!
task
?.
taskId
)
return
setGeneratedVideo
(
null
)
startPolling
({
...
task
,
status
:
'PROCESSING'
,
error
:
null
})
},
[
activeTask
,
startPolling
],
)
const
retryTask
=
useCallback
(
(
task
=
activeTask
)
=>
{
if
(
!
task
?.
options
)
return
Promise
.
reject
(
new
Error
(
'缺少原始生成参数'
))
return
generateVideo
(
task
.
options
)
},
[
activeTask
,
generateVideo
],
)
)
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -161,6 +305,50 @@ export default function useDigitalHuman() {
...
@@ -161,6 +305,50 @@ export default function useDigitalHuman() {
return
()
=>
stopPolling
()
return
()
=>
stopPolling
()
},
[
fetchFigures
,
stopPolling
])
},
[
fetchFigures
,
stopPolling
])
useEffect
(()
=>
{
let
active
=
true
getDigitalHumanVoiceList
()
.
then
((
list
)
=>
{
if
(
!
active
)
return
const
voices
=
(
Array
.
isArray
(
list
)
?
list
:
[])
.
map
(
normalizeDigitalHumanVoice
)
.
filter
((
voice
)
=>
voice
.
id
)
.
map
((
voice
)
=>
{
if
(
voice
.
previewUrl
)
return
voice
// 系统音色的 exampleAudioUrl 可能为空,按 perId 回退到静态试听地址
const
fallback
=
[...
staticFemaleVoices
,
...
staticMaleVoices
].
find
(
(
item
)
=>
String
(
item
.
id
)
===
String
(
voice
.
id
),
)
return
fallback
?
{
...
voice
,
previewUrl
:
fallback
.
previewUrl
}
:
voice
})
if
(
voices
.
length
===
0
)
{
setFemaleVoices
(
staticFemaleVoices
)
setMaleVoices
(
staticMaleVoices
)
return
}
setFemaleVoices
(
voices
.
filter
((
voice
)
=>
voice
.
gender
===
'女声'
))
setMaleVoices
(
voices
.
filter
((
voice
)
=>
voice
.
gender
===
'男声'
))
})
.
catch
((
err
)
=>
{
console
.
error
(
'Fetch Voices Error:'
,
err
)
if
(
active
)
{
setFemaleVoices
(
staticFemaleVoices
)
setMaleVoices
(
staticMaleVoices
)
}
})
return
()
=>
{
active
=
false
}
},
[])
useEffect
(()
=>
{
const
unfinishedTask
=
readDigitalHumanTasks
().
find
((
task
)
=>
task
.
status
===
'PROCESSING'
)
if
(
unfinishedTask
)
startPolling
(
unfinishedTask
)
},
[
startPolling
])
return
{
return
{
figures
,
figures
,
femaleVoices
,
femaleVoices
,
...
@@ -169,7 +357,13 @@ export default function useDigitalHuman() {
...
@@ -169,7 +357,13 @@ export default function useDigitalHuman() {
isGenerating
,
isGenerating
,
generatedVideo
,
generatedVideo
,
error
,
error
,
tasks
,
activeTask
,
generateVideo
,
generateVideo
,
selectTask
,
resumeTask
,
retryTask
,
refreshFigures
:
fetchFigures
,
resetGeneratedVideo
:
()
=>
setGeneratedVideo
(
null
),
resetGeneratedVideo
:
()
=>
setGeneratedVideo
(
null
),
}
}
}
}
apps/admin/src/components/editor/wangeditor-customer/__tests__/digital-human-request.test.ts
0 → 100644
浏览文件 @
72492e6c
import
{
describe
,
expect
,
it
}
from
'vitest'
import
{
buildDigitalHumanRequest
,
DIGITAL_HUMAN_TYPES
}
from
'../../hooks/digitalHumanRequest'
const
common
=
{
driveType
:
'TEXT'
,
text
:
'数字人测试。'
,
voiceId
:
'5116'
,
language
:
'Chinese'
,
speed
:
5
,
volume
:
6
,
pitch
:
4
,
}
describe
(
'digital human request builder'
,
()
=>
{
it
(
'builds the basic video contract'
,
()
=>
{
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
BASIC
,
{
...
common
,
figureId
:
'1081'
,
width
:
720
,
height
:
1280
,
transparent
:
false
,
enableSubtitle
:
true
,
positionX
:
0
,
positionY
:
0.2
,
positionZ
:
1
,
}),
).
toMatchObject
({
figureId
:
'1081'
,
driveType
:
'TEXT'
,
ttsParams
:
{
person
:
'5116'
,
speed
:
'5'
,
volume
:
'6'
,
pitch
:
'4'
},
videoParams
:
{
width
:
720
,
height
:
1280
,
transparent
:
false
},
dhParams
:
{
position
:
{
x
:
0
,
y
:
0.2
,
z
:
1
}
},
subtitleParams
:
{
enabled
:
true
,
subtitlePolicy
:
'SRT'
},
})
})
it
(
'builds the advanced video media and fission contract'
,
()
=>
{
const
result
=
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
ADVANCED
,
{
...
common
,
figureId
:
'1081'
,
templateId
:
'template-1'
,
width
:
1080
,
height
:
1920
,
model
:
'turbo_v2'
,
title
:
'标题'
,
logoUrl
:
'https://example.com/logo.png'
,
bgmUrl
:
'https://example.com/bgm.mp3'
,
materialUrl
:
'https://example.com/material.mp4'
,
extendBackgroundUrl
:
'https://example.com/extend.png'
,
riskTip
:
'示例水印'
,
openingFileId
:
'opening-file-id'
,
endingFileId
:
'ending-file-id'
,
mashupMaterials
:
[
{
fileId
:
'image-file-id'
,
fileName
:
'背景.jpg'
,
mediaType
:
'IMAGE'
},
{
fileId
:
'video-file-id'
,
fileName
:
'背景.mp4'
,
mediaType
:
'VIDEO'
},
],
fissionFigureIds
:
[
'1081'
,
'1112'
],
fissionVoiceIds
:
[
'5116'
],
})
expect
(
result
).
toMatchObject
({
figureId
:
'1081'
,
templateId
:
'template-1'
,
model
:
'turbo_v2'
,
title
:
'标题'
,
logoParams
:
{
logoUrl
:
'https://example.com/logo.png'
},
bgmParams
:
{
bgmUrl
:
'https://example.com/bgm.mp3'
},
materialUrl
:
'https://example.com/material.mp4'
,
extendBackgroundUrl
:
'https://example.com/extend.png'
,
riskTip
:
'示例水印'
,
openingMaterial
:
{
fileId
:
'opening-file-id'
,
mediaType
:
'VIDEO'
},
endingMaterial
:
{
fileId
:
'ending-file-id'
,
mediaType
:
'VIDEO'
},
mashupMaterials
:
[
{
fileId
:
'image-file-id'
,
mediaType
:
'IMAGE'
},
{
fileId
:
'video-file-id'
,
mediaType
:
'VIDEO'
},
],
fissionParams
:
{
figureIds
:
[
'1081'
,
'1112'
],
ttsPersons
:
[
'5116'
]
},
})
expect
(
result
).
not
.
toHaveProperty
(
'ttsParams.lan'
)
})
it
(
'does not enable fission without figure IDs'
,
()
=>
{
const
result
=
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
ADVANCED
,
{
...
common
,
figureId
:
'1081'
,
templateId
:
'template-1'
,
width
:
1080
,
height
:
1920
,
fissionVoiceIds
:
[
'5116'
],
})
expect
(
result
).
not
.
toHaveProperty
(
'fissionParams'
)
})
it
(
'supports the basic camera and pixel positioning contracts'
,
()
=>
{
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
BASIC
,
{
...
common
,
figureId
:
'1081'
,
width
:
1080
,
height
:
1920
,
positionMode
:
'camera'
,
cameraId
:
3
,
}),
).
toMatchObject
({
dhParams
:
{
cameraId
:
3
}
})
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
BASIC
,
{
...
common
,
figureId
:
'1081'
,
width
:
1080
,
height
:
1920
,
positionMode
:
'pixel'
,
cropTop
:
10
,
cropLeft
:
20
,
cropWidth
:
900
,
cropHeight
:
1600
,
locationTop
:
30
,
locationLeft
:
40
,
locationWidth
:
720
,
locationHeight
:
1280
,
}),
).
toMatchObject
({
dhParams
:
{
positionV2
:
{
crop
:
{
top
:
10
,
left
:
20
,
width
:
900
,
height
:
1600
},
location
:
{
top
:
30
,
left
:
40
,
width
:
720
,
height
:
1280
},
},
},
})
})
it
(
'uses the visual preview location for 2D small-sample figures'
,
()
=>
{
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
BASIC
,
{
...
common
,
figureId
:
'1081'
,
width
:
1920
,
height
:
1080
,
dhParams
:
{
positionV2
:
{
location
:
{
top
:
-
39
,
left
:
525
,
width
:
1920
,
height
:
1080
},
},
},
}),
).
toMatchObject
({
dhParams
:
{
positionV2
:
{
location
:
{
top
:
-
39
,
left
:
525
,
width
:
1920
,
height
:
1080
},
},
},
})
})
it
(
'builds 123 and photo video contracts with their own inputs'
,
()
=>
{
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
FAST
,
{
...
common
,
templateVideoId
:
'https://example.com/123.mp4'
,
}),
).
toMatchObject
({
templateVideoId
:
'https://example.com/123.mp4'
,
driveType
:
'TEXT'
})
expect
(
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
PHOTO
,
{
...
common
,
inputImageUrl
:
'https://example.com/photo.jpg'
,
model
:
'turbo_v2'
,
enableSubtitle
:
true
,
}),
).
toMatchObject
({
inputImageUrl
:
'https://example.com/photo.jpg'
,
model
:
'turbo_v2'
,
subtitleParams
:
{
enabled
:
true
},
})
})
it
(
'uses input audio instead of TTS parameters for voice drive'
,
()
=>
{
const
result
=
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
PHOTO
,
{
driveType
:
'VOICE'
,
inputAudioUrl
:
'https://example.com/voice.mp3'
,
inputImageUrl
:
'https://example.com/photo.jpg'
,
})
expect
(
result
).
toMatchObject
({
driveType
:
'VOICE'
,
inputAudioUrl
:
expect
.
any
(
String
),
subtitleParams
:
{
enabled
:
false
},
})
expect
(
result
).
not
.
toHaveProperty
(
'ttsParams'
)
})
it
(
'omits options that do not take effect in the selected mode'
,
()
=>
{
const
result
=
buildDigitalHumanRequest
(
DIGITAL_HUMAN_TYPES
.
BASIC
,
{
driveType
:
'VOICE'
,
figureId
:
'1081'
,
inputAudioUrl
:
'https://example.com/voice.mp3'
,
width
:
1080
,
height
:
1920
,
transparent
:
true
,
backgroundImageUrl
:
'https://example.com/background.jpg'
,
autoAnimoji
:
true
,
enableSubtitle
:
true
,
})
expect
(
result
).
not
.
toHaveProperty
(
'backgroundImageUrl'
)
expect
(
result
).
toMatchObject
({
autoAnimoji
:
false
,
subtitleParams
:
{
enabled
:
false
},
})
})
})
apps/admin/src/components/editor/wangeditor-customer/__tests__/digital-human-task-store.test.ts
0 → 100644
浏览文件 @
72492e6c
import
{
describe
,
expect
,
it
}
from
'vitest'
import
{
clearDigitalHumanTasks
,
readDigitalHumanTasks
,
saveDigitalHumanTask
,
}
from
'../../hooks/digitalHumanTaskStore'
function
createStorage
()
{
const
values
=
new
Map
<
string
,
string
>
()
return
{
getItem
:
(
key
:
string
)
=>
values
.
get
(
key
)
??
null
,
setItem
:
(
key
:
string
,
value
:
string
)
=>
values
.
set
(
key
,
value
),
removeItem
:
(
key
:
string
)
=>
values
.
delete
(
key
),
}
}
describe
(
'digital human task storage'
,
()
=>
{
it
(
'keeps the newest version of a task first'
,
()
=>
{
const
storage
=
createStorage
()
saveDigitalHumanTask
({
taskId
:
'task-1'
,
status
:
'PROCESSING'
},
storage
)
saveDigitalHumanTask
({
taskId
:
'task-2'
,
status
:
'SUCCESS'
},
storage
)
saveDigitalHumanTask
({
taskId
:
'task-1'
,
status
:
'FAILED'
},
storage
)
expect
(
readDigitalHumanTasks
(
storage
)).
toEqual
([
{
taskId
:
'task-1'
,
status
:
'FAILED'
},
{
taskId
:
'task-2'
,
status
:
'SUCCESS'
},
])
})
it
(
'keeps at most ten recent tasks and can clear them'
,
()
=>
{
const
storage
=
createStorage
()
for
(
let
index
=
0
;
index
<
12
;
index
+=
1
)
{
saveDigitalHumanTask
({
taskId
:
`task-
${
index
}
`
,
status
:
'SUCCESS'
},
storage
)
}
expect
(
readDigitalHumanTasks
(
storage
)).
toHaveLength
(
10
)
expect
(
readDigitalHumanTasks
(
storage
)[
0
].
taskId
).
toBe
(
'task-11'
)
clearDigitalHumanTasks
(
storage
)
expect
(
readDigitalHumanTasks
(
storage
)).
toEqual
([])
})
it
(
'ignores invalid stored data'
,
()
=>
{
const
storage
=
createStorage
()
storage
.
setItem
(
'center-book:digital-human-tasks'
,
'{invalid'
)
expect
(
readDigitalHumanTasks
(
storage
)).
toEqual
([])
})
it
(
'does not break generation when browser storage is unavailable'
,
()
=>
{
const
storage
=
createStorage
()
storage
.
setItem
=
()
=>
{
throw
new
Error
(
'storage unavailable'
)
}
expect
(
saveDigitalHumanTask
({
taskId
:
'task-1'
,
status
:
'PROCESSING'
},
storage
)).
toEqual
([])
})
})
apps/admin/src/components/editor/wangeditor-customer/features/ai-media/DigitalHumanBaiduUploadField.jsx
0 → 100644
浏览文件 @
72492e6c
import
{
useState
}
from
'react'
import
{
App
,
Button
,
Upload
}
from
'antd'
import
{
DeleteOutlined
,
UploadOutlined
}
from
'@ant-design/icons'
import
{
uploadDigitalHumanFile
}
from
'@/components/editor/api/xiling'
const
PROVIDER_TYPE
=
'OPEN_VIDEO_DURATION_VIDEO'
const
IMAGE_EXTENSIONS
=
[
'png'
,
'jpg'
,
'jpeg'
,
'bmp'
]
function
getExtension
(
file
)
{
return
file
.
name
.
split
(
'.'
).
pop
()?.
toLowerCase
()
||
''
}
function
getMediaType
(
file
)
{
return
file
.
type
.
startsWith
(
'image/'
)
||
IMAGE_EXTENSIONS
.
includes
(
getExtension
(
file
))
?
'IMAGE'
:
'VIDEO'
}
function
matchesAccept
(
file
,
accept
)
{
const
extension
=
getExtension
(
file
)
return
accept
.
split
(
','
).
some
((
item
)
=>
{
const
pattern
=
item
.
trim
().
toLowerCase
()
if
(
pattern
.
startsWith
(
'.'
))
return
pattern
===
`.
${
extension
}
`
if
(
pattern
.
endsWith
(
'/*'
))
return
file
.
type
.
startsWith
(
pattern
.
slice
(
0
,
-
1
))
return
file
.
type
.
toLowerCase
()
===
pattern
})
}
function
getFileLimit
(
file
)
{
return
getMediaType
(
file
)
===
'IMAGE'
?
3
:
500
}
export
default
function
DigitalHumanBaiduUploadField
({
label
,
value
,
onChange
,
accept
,
multiple
=
false
,
maxCount
=
1
,
hint
,
})
{
const
{
message
}
=
App
.
useApp
()
const
[
pendingUploads
,
setPendingUploads
]
=
useState
(
0
)
const
uploading
=
pendingUploads
>
0
const
files
=
multiple
?
value
||
[]
:
value
?
[
value
]
:
[]
const
uploadDisabled
=
uploading
||
(
multiple
&&
files
.
length
>=
maxCount
)
const
upload
=
async
({
file
,
onSuccess
,
onError
})
=>
{
if
(
!
matchesAccept
(
file
,
accept
))
{
const
error
=
new
Error
(
'文件格式不符合要求'
)
message
.
error
(
error
.
message
)
onError
(
error
)
return
}
const
limit
=
getFileLimit
(
file
)
if
(
file
.
size
>
limit
*
1024
*
1024
)
{
const
error
=
new
Error
(
`
${
getMediaType
(
file
)
===
'IMAGE'
?
'图片'
:
'视频'
}
不能超过
${
limit
}
MB`
,
)
message
.
error
(
error
.
message
)
onError
(
error
)
return
}
setPendingUploads
((
current
)
=>
current
+
1
)
try
{
const
result
=
await
uploadDigitalHumanFile
(
file
,
PROVIDER_TYPE
)
if
(
!
result
?.
fileId
)
throw
new
Error
(
'上传结果缺少文件编号'
)
const
nextFile
=
{
fileId
:
result
.
fileId
,
fileName
:
result
.
fileName
||
file
.
name
,
mediaType
:
getMediaType
(
file
),
}
onChange
(
multiple
?
(
current
)
=>
[...(
current
||
[]),
nextFile
].
slice
(
0
,
maxCount
)
:
nextFile
)
onSuccess
(
result
)
message
.
success
(
`
${
label
}
上传成功`
)
}
catch
(
error
)
{
message
.
error
(
`
${
label
}
上传失败`
)
onError
(
error
)
}
finally
{
setPendingUploads
((
current
)
=>
Math
.
max
(
0
,
current
-
1
))
}
}
const
remove
=
(
index
)
=>
{
if
(
multiple
)
onChange
(
files
.
filter
((
_
,
itemIndex
)
=>
itemIndex
!==
index
))
else
onChange
(
null
)
}
return
(
<
div
className=
'media-field baidu-upload-field'
>
<
div
className=
'field-label'
>
{
label
}
</
div
>
<
Upload
accept=
{
accept
}
multiple=
{
multiple
}
maxCount=
{
maxCount
}
showUploadList=
{
false
}
customRequest=
{
upload
}
disabled=
{
uploadDisabled
}
>
<
Button
icon=
{
<
UploadOutlined
/>
}
loading=
{
uploading
}
disabled=
{
uploadDisabled
}
>
{
multiple
?
`添加${label}(${files.length}/${maxCount})`
:
files
.
length
?
`更换${label}`
:
`上传${label}`
}
</
Button
>
</
Upload
>
{
files
.
length
?
(
<
div
className=
'baidu-upload-list'
>
{
files
.
map
((
file
,
index
)
=>
(
<
div
className=
'baidu-upload-item'
key=
{
file
.
fileId
}
>
<
span
>
{
file
.
fileName
}
</
span
>
<
Button
type=
'text'
size=
'small'
icon=
{
<
DeleteOutlined
/>
}
aria
-
label=
{
`删除${file.fileName}`
}
onClick=
{
()
=>
remove
(
index
)
}
/>
</
div
>
))
}
</
div
>
)
:
null
}
{
hint
?
<
div
className=
'field-hint'
>
{
hint
}
</
div
>
:
null
}
</
div
>
)
}
apps/admin/src/components/editor/wangeditor-customer/features/ai-media/DigitalHumanCustomizePanel.jsx
0 → 100644
浏览文件 @
72492e6c
import
{
useState
}
from
'react'
import
{
Alert
,
App
,
Button
,
Form
,
Input
,
Radio
,
Tag
}
from
'antd'
import
{
ReloadOutlined
}
from
'@ant-design/icons'
import
{
queryGenerativeFigure
,
submitGenerativeFigure
}
from
'@/components/editor/api/xiling'
import
DigitalHumanUploadField
from
'./DigitalHumanUploadField'
const
customizeStatus
=
{
LINE_UP
:
{
color
:
'default'
,
text
:
'排队中'
},
ACTIONING
:
{
color
:
'processing'
,
text
:
'动作生成中'
},
GENERATING
:
{
color
:
'processing'
,
text
:
'训练中'
},
SUCCESS
:
{
color
:
'success'
,
text
:
'已完成'
},
FAILED
:
{
color
:
'error'
,
text
:
'定制失败'
},
}
function
findFigure
(
response
,
figureId
)
{
const
payload
=
response
?.
result
const
list
=
Array
.
isArray
(
payload
?.
result
)
?
payload
.
result
:
Array
.
isArray
(
payload
)
?
payload
:
payload
?
[
payload
]
:
[]
return
list
.
find
((
item
)
=>
String
(
item
.
figureId
)
===
String
(
figureId
))
||
list
[
0
]
}
export
default
function
DigitalHumanCustomizePanel
({
onSuccess
})
{
const
{
message
}
=
App
.
useApp
()
const
[
imageUrl
,
setImageUrl
]
=
useState
(
''
)
const
[
task
,
setTask
]
=
useState
(
null
)
const
[
submitting
,
setSubmitting
]
=
useState
(
false
)
const
[
querying
,
setQuerying
]
=
useState
(
false
)
const
[
error
,
setError
]
=
useState
(
''
)
const
handleSubmit
=
async
(
values
)
=>
{
if
(
!
imageUrl
)
return
message
.
warning
(
'请先上传参考图片'
)
setSubmitting
(
true
)
setError
(
''
)
try
{
const
response
=
await
submitGenerativeFigure
({
name
:
values
.
name
.
trim
(),
customizeType
:
'GENERATE_NO_ACTION'
,
gender
:
values
.
gender
,
figureImageUrl
:
imageUrl
,
})
const
figureId
=
response
.
result
?.
figureId
if
(
!
figureId
)
throw
new
Error
(
'未获取到形象 ID'
)
setTask
({
figureId
,
name
:
values
.
name
.
trim
(),
status
:
'LINE_UP'
})
message
.
success
(
'形象定制任务已提交'
)
}
catch
(
submitError
)
{
setError
(
submitError
.
response
?.
data
?.
message
?.
global
||
submitError
.
message
||
'提交失败'
)
}
finally
{
setSubmitting
(
false
)
}
}
const
handleQuery
=
async
()
=>
{
if
(
!
task
?.
figureId
)
return
setQuerying
(
true
)
setError
(
''
)
try
{
const
response
=
await
queryGenerativeFigure
({
figureId
:
task
.
figureId
})
const
figure
=
findFigure
(
response
,
task
.
figureId
)
if
(
!
figure
)
throw
new
Error
(
'暂未查询到形象任务'
)
const
nextTask
=
{
...
task
,
...
figure
}
setTask
(
nextTask
)
if
(
nextTask
.
status
===
'SUCCESS'
)
onSuccess
?.()
}
catch
(
queryError
)
{
setError
(
queryError
.
response
?.
data
?.
message
?.
global
||
queryError
.
message
||
'查询失败'
)
}
finally
{
setQuerying
(
false
)
}
}
return
(
<
div
className=
'customize-layout'
>
<
section
className=
'customize-form-card'
>
<
h2
>
生成式数字人形象定制
</
h2
>
<
p
>
上传一张人物图片,生成可用于视频合成的数字人形象。
</
p
>
<
Form
layout=
'vertical'
initialValues=
{
{
gender
:
'MALE'
}
}
requiredMark=
{
false
}
onFinish=
{
handleSubmit
}
>
<
Form
.
Item
label=
'形象名称'
name=
'name'
rules=
{
[
{
required
:
true
,
whitespace
:
true
,
message
:
'请输入形象名称'
},
{
max
:
20
,
message
:
'形象名称不能超过20个字符'
},
]
}
>
<
Input
maxLength=
{
20
}
placeholder=
'请输入形象名称'
/>
</
Form
.
Item
>
<
Form
.
Item
label=
'性别'
name=
'gender'
>
<
Radio
.
Group
optionType=
'button'
buttonStyle=
'solid'
options=
{
[
{
label
:
'男'
,
value
:
'MALE'
},
{
label
:
'女'
,
value
:
'FEMALE'
},
]
}
/>
</
Form
.
Item
>
<
DigitalHumanUploadField
label=
'参考图片'
value=
{
imageUrl
}
onChange=
{
setImageUrl
}
accept=
'image/jpeg,image/png'
maxSizeMB=
{
10
}
filenamePrefix=
'digital-human-figure'
previewType=
'image'
hint=
'支持 JPG、JPEG、PNG,建议使用 1080×1920 或 1920×1080 图片。'
/>
<
Button
type=
'primary'
htmlType=
'submit'
loading=
{
submitting
}
block
>
开始定制
</
Button
>
</
Form
>
</
section
>
<
section
className=
'customize-result-card'
>
<
div
className=
'customize-result-heading'
>
<
h2
>
定制进度
</
h2
>
{
task
?
(
<
Button
icon=
{
<
ReloadOutlined
/>
}
onClick=
{
handleQuery
}
loading=
{
querying
}
>
查询进度
</
Button
>
)
:
null
}
</
div
>
{
error
?
<
Alert
type=
'error'
showIcon
title=
{
error
}
/>
:
null
}
{
task
?
(
<
div
className=
'customize-task'
>
{
task
.
templateImg
||
task
.
pictureUrl
||
imageUrl
?
(
<
img
src=
{
task
.
templateImg
||
task
.
pictureUrl
||
imageUrl
}
alt=
{
`${task.name || '数字人'}形象预览`
}
/>
)
:
null
}
<
div
>
<
strong
>
{
task
.
name
||
'数字人形象'
}
</
strong
>
<
span
>
ID:
{
task
.
figureId
}
</
span
>
<
Tag
color=
{
customizeStatus
[
task
.
status
]?.
color
}
>
{
customizeStatus
[
task
.
status
]?.
text
||
task
.
status
}
</
Tag
>
{
task
.
failedMessage
?
<
small
>
{
task
.
failedMessage
}
</
small
>
:
null
}
</
div
>
</
div
>
)
:
(
<
div
className=
'customize-empty'
>
提交任务后,可在这里查询训练进度。
</
div
>
)
}
</
section
>
</
div
>
)
}
apps/admin/src/components/editor/wangeditor-customer/features/ai-media/DigitalHumanDialog.jsx
浏览文件 @
72492e6c
import
{
useState
,
useRef
,
useEffect
}
from
'react'
import
{
useState
,
useRef
,
useEffect
,
useMemo
}
from
'react'
import
{
import
{
Modal
,
Modal
,
Input
,
Input
,
InputNumber
,
Button
,
Button
,
message
,
message
,
Spin
,
Spin
,
Row
,
Select
,
Col
,
Space
,
Space
,
Divider
,
Checkbox
,
Tabs
,
Empty
,
Empty
,
Alert
,
Tag
,
Collapse
,
Slider
,
Tabs
,
}
from
'antd'
}
from
'antd'
import
{
import
{
VideoCameraOutlined
,
CheckOutlined
,
CheckOutlined
,
CopyOutlined
,
DownloadOutlined
,
DownloadOutlined
,
PlayCircleOutlined
,
PauseCircleOutlined
,
PauseCircleOutlined
,
PlayCircleOutlined
,
ReloadOutlined
,
SwapOutlined
,
UserOutlined
,
UserOutlined
,
}
from
'@ant-design/icons'
}
from
'@ant-design/icons'
import
{
SlateTransforms
}
from
'@wangeditor/editor'
import
{
SlateTransforms
}
from
'@wangeditor/editor'
import
{
saveAs
}
from
'file-saver'
import
{
saveAs
}
from
'file-saver'
import
{
uploadFileByUrl
}
from
'@/utils/oss'
import
{
uploadFileByUrl
}
from
'@/utils/oss'
import
useDigitalHuman
from
'@/components/editor/hooks/useDigitalHuman'
import
useDigitalHuman
from
'@/components/editor/hooks/useDigitalHuman'
import
{
ADVANCED_TEMPLATES
,
DIGITAL_HUMAN_TYPES
,
DIGITAL_HUMAN_TYPE_LABELS
,
getConfiguredDigitalHumanType
,
}
from
'@/components/editor/hooks/digitalHumanRequest'
import
DigitalHumanUploadField
from
'./DigitalHumanUploadField'
import
DigitalHumanCustomizePanel
from
'./DigitalHumanCustomizePanel'
import
DigitalHumanBaiduUploadField
from
'./DigitalHumanBaiduUploadField'
import
'./digital-human.less'
import
'./digital-human.less'
import
{
import
{
insertBlockNodesAndFocus
,
insertBlockNodesAndFocus
,
...
@@ -32,18 +45,121 @@ import {
...
@@ -32,18 +45,121 @@ import {
}
from
'../../shared/editor-operations'
}
from
'../../shared/editor-operations'
const
{
TextArea
}
=
Input
const
{
TextArea
}
=
Input
const
videoSizes
=
{
portrait
:
{
width
:
1080
,
height
:
1920
},
landscape
:
{
width
:
1920
,
height
:
1080
},
}
function
getFigureVideoSize
(
figure
)
{
const
width
=
Number
(
figure
?.
resolutionWidth
)
const
height
=
Number
(
figure
?.
resolutionHeight
)
if
(
!
width
||
!
height
)
return
null
return
width
>=
height
?
'landscape'
:
'portrait'
}
function
getFigureLayerSize
(
canvasSize
,
figure
,
imageAspect
=
null
)
{
const
figureWidth
=
Number
(
figure
?.
resolutionWidth
)
const
figureHeight
=
Number
(
figure
?.
resolutionHeight
)
// 宽高比优先取接口分辨率;没有时取图片实际加载出的宽高比(imageAspect = 高/宽)
const
figureRatio
=
figureWidth
&&
figureHeight
?
figureWidth
/
figureHeight
:
imageAspect
?
1
/
imageAspect
:
null
if
(
!
figureRatio
)
return
canvasSize
const
canvasRatio
=
canvasSize
.
width
/
canvasSize
.
height
if
(
figureRatio
<
canvasRatio
)
{
return
{
width
:
Math
.
max
(
2
,
Math
.
round
(
canvasSize
.
height
*
figureRatio
)),
height
:
canvasSize
.
height
,
}
}
return
{
width
:
canvasSize
.
width
,
height
:
Math
.
max
(
2
,
Math
.
round
(
canvasSize
.
width
/
figureRatio
)),
}
}
function
buildPreviewDhParams
(
size
,
layout
,
figure
,
imageAspect
=
null
)
{
const
layerSize
=
getFigureLayerSize
(
size
,
figure
,
imageAspect
)
const
width
=
Math
.
max
(
2
,
Math
.
round
((
layerSize
.
width
*
layout
.
scale
)
/
2
)
*
2
)
const
height
=
Math
.
max
(
2
,
Math
.
round
((
layerSize
.
height
*
layout
.
scale
)
/
2
)
*
2
)
return
{
positionV2
:
{
location
:
{
top
:
Math
.
round
((
size
.
height
*
layout
.
y
)
/
100
-
height
/
2
),
left
:
Math
.
round
((
size
.
width
*
layout
.
x
)
/
100
-
width
/
2
),
width
,
height
,
},
},
}
}
const
DEFAULT_MODEL
=
'quality_v2'
const
taskStatus
=
{
PROCESSING
:
{
color
:
'processing'
,
text
:
'生成中'
},
SUCCESS
:
{
color
:
'success'
,
text
:
'已完成'
},
FAILED
:
{
color
:
'error'
,
text
:
'生成失败'
},
PAUSED
:
{
color
:
'warning'
,
text
:
'查询已暂停'
},
}
const
CUSTOMIZE_WORKSPACE
=
'customize'
const
workspaceTabs
=
[
{
key
:
DIGITAL_HUMAN_TYPES
.
BASIC
,
label
:
'基础视频合成'
},
{
key
:
DIGITAL_HUMAN_TYPES
.
ADVANCED
,
label
:
'高级视频合成'
},
{
key
:
CUSTOMIZE_WORKSPACE
,
label
:
'形象定制'
},
]
export
default
function
AIDigitalHumanModal
(
props
)
{
export
default
function
AIDigitalHumanModal
(
props
)
{
const
{
editor
}
=
props
const
{
editor
}
=
props
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
true
)
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
true
)
// 核心选择状态
const
[
selectedFigure
,
setSelectedFigure
]
=
useState
(
null
)
const
[
selectedFigure
,
setSelectedFigure
]
=
useState
(
null
)
const
[
selectedVoice
,
setSelectedVoice
]
=
useState
(
null
)
const
[
selectedVoice
,
setSelectedVoice
]
=
useState
(
null
)
// 已移除字幕、透明背景和背景图片 URL 相关配置
const
[
textValue
,
setTextValue
]
=
useState
(
''
)
const
[
textValue
,
setTextValue
]
=
useState
(
''
)
const
[
playingVoiceId
,
setPlayingVoiceId
]
=
useState
(
null
)
const
[
playingVoiceId
,
setPlayingVoiceId
]
=
useState
(
null
)
const
[
videoSize
,
setVideoSize
]
=
useState
(
getConfiguredDigitalHumanType
()
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
ADVANCED_TEMPLATES
[
0
].
ratio
:
'landscape'
,
)
const
[
isInserting
,
setIsInserting
]
=
useState
(
false
)
const
[
activeMode
,
setActiveMode
]
=
useState
(
getConfiguredDigitalHumanType
)
const
type
=
activeMode
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
DIGITAL_HUMAN_TYPES
.
ADVANCED
:
DIGITAL_HUMAN_TYPES
.
BASIC
const
[
language
,
setLanguage
]
=
useState
(
'auto'
)
const
[
speed
,
setSpeed
]
=
useState
(
5
)
const
[
volume
,
setVolume
]
=
useState
(
5
)
const
[
pitch
,
setPitch
]
=
useState
(
5
)
const
[
figurePickerOpen
,
setFigurePickerOpen
]
=
useState
(
false
)
const
[
figureFilters
,
setFigureFilters
]
=
useState
({
gender
:
'all'
,
style
:
'all'
,
pose
:
'all'
,
background
:
'all'
,
})
const
[
voicePickerOpen
,
setVoicePickerOpen
]
=
useState
(
false
)
const
[
voiceGender
,
setVoiceGender
]
=
useState
(
'female'
)
const
[
templateId
,
setTemplateId
]
=
useState
(
ADVANCED_TEMPLATES
[
0
].
value
)
const
[
model
,
setModel
]
=
useState
(
DEFAULT_MODEL
)
const
[
title
,
setTitle
]
=
useState
(
''
)
const
[
logoUrl
,
setLogoUrl
]
=
useState
(
''
)
const
[
bgmUrl
,
setBgmUrl
]
=
useState
(
''
)
const
[
backgroundImageUrl
,
setBackgroundImageUrl
]
=
useState
(
''
)
const
[
materialUrl
,
setMaterialUrl
]
=
useState
(
''
)
const
[
extendBackgroundUrl
,
setExtendBackgroundUrl
]
=
useState
(
''
)
const
[
riskTip
,
setRiskTip
]
=
useState
(
''
)
const
[
openingMaterial
,
setOpeningMaterial
]
=
useState
(
null
)
const
[
endingMaterial
,
setEndingMaterial
]
=
useState
(
null
)
const
[
mashupMaterials
,
setMashupMaterials
]
=
useState
([])
const
[
fissionFigureIds
,
setFissionFigureIds
]
=
useState
([])
const
[
fissionVoiceIds
,
setFissionVoiceIds
]
=
useState
([])
const
audioRef
=
useRef
(
null
)
const
previewDragRef
=
useRef
(
null
)
const
[
previewImageAspect
,
setPreviewImageAspect
]
=
useState
(
null
)
const
[
showGeneratedPreview
,
setShowGeneratedPreview
]
=
useState
(
false
)
const
[
previewFigureSelected
,
setPreviewFigureSelected
]
=
useState
(
false
)
const
[
selectedHistoryTaskId
,
setSelectedHistoryTaskId
]
=
useState
(
null
)
const
{
const
{
figures
,
figures
,
...
@@ -52,51 +168,422 @@ export default function AIDigitalHumanModal(props) {
...
@@ -52,51 +168,422 @@ export default function AIDigitalHumanModal(props) {
loadingFigures
,
loadingFigures
,
isGenerating
,
isGenerating
,
generatedVideo
,
generatedVideo
,
error
,
tasks
,
activeTask
,
generateVideo
,
generateVideo
,
selectTask
,
resumeTask
,
retryTask
,
refreshFigures
,
}
=
useDigitalHuman
()
}
=
useDigitalHuman
()
// 初始化默认选择
const
maxTextLength
=
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
2000
:
20000
useEffect
(()
=>
{
const
requiredItems
=
[
if
(
!
selectedFigure
&&
figures
.
length
>
0
)
{
{
label
:
'选择 2D 小样本数字人'
,
done
:
!!
selectedFigure
},
setSelectedFigure
(
figures
[
0
])
...(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
[{
label
:
'选择视频样式'
,
done
:
!!
templateId
}]
:
[]),
{
label
:
'填写讲课内容'
,
done
:
!!
textValue
.
trim
()
},
{
label
:
'确认配音'
,
done
:
!!
selectedVoice
},
]
const
nextRequiredItem
=
requiredItems
.
find
((
item
)
=>
!
item
.
done
)?.
label
const
canGenerate
=
requiredItems
.
every
((
item
)
=>
item
.
done
)
const
selectedTemplate
=
ADVANCED_TEMPLATES
.
find
((
item
)
=>
item
.
value
===
templateId
)
const
allVoices
=
useMemo
(()
=>
[...
femaleVoices
,
...
maleVoices
],
[
femaleVoices
,
maleVoices
])
const
figureVideoSize
=
getFigureVideoSize
(
selectedFigure
)
const
outputVideoSize
=
videoSize
const
figureFilterOptions
=
useMemo
(()
=>
{
const
styles
=
new
Set
()
const
poses
=
new
Set
()
const
backgrounds
=
new
Set
()
figures
.
forEach
((
figure
)
=>
{
const
tags
=
figure
.
tags
||
[]
tags
.
forEach
((
tag
)
=>
{
if
(
!
[
'视频'
,
'2D'
].
includes
(
tag
))
styles
.
add
(
tag
)
})
if
(
figure
.
pose
)
poses
.
add
(
figure
.
pose
)
if
(
figure
.
background
)
backgrounds
.
add
(
figure
.
background
)
})
return
{
styles
:
[...
styles
],
poses
:
[...
poses
],
backgrounds
:
[...
backgrounds
],
}
}
if
(
!
selectedVoice
&&
femaleVoices
.
length
>
0
)
{
},
[
figures
])
setSelectedVoice
(
femaleVoices
[
0
])
const
filteredFigures
=
useMemo
(
()
=>
figures
.
filter
((
figure
)
=>
{
const
gender
=
String
(
figure
.
gender
||
''
).
toLowerCase
()
const
matchesGender
=
figureFilters
.
gender
===
'all'
||
gender
===
figureFilters
.
gender
||
(
figureFilters
.
gender
===
'female'
&&
gender
===
'f'
)
||
(
figureFilters
.
gender
===
'male'
&&
gender
===
'm'
)
const
matchesStyle
=
figureFilters
.
style
===
'all'
||
(
figure
.
tags
||
[]).
includes
(
figureFilters
.
style
)
const
matchesPose
=
figureFilters
.
pose
===
'all'
||
figure
.
pose
===
figureFilters
.
pose
const
matchesBackground
=
figureFilters
.
background
===
'all'
||
figure
.
background
===
figureFilters
.
background
return
matchesGender
&&
matchesStyle
&&
matchesPose
&&
matchesBackground
}),
[
figureFilters
,
figures
],
)
const
visibleTasks
=
useMemo
(()
=>
tasks
.
filter
((
task
)
=>
task
.
type
===
type
),
[
tasks
,
type
])
const
activeVideoTask
=
activeTask
?.
type
===
type
?
activeTask
:
null
const
previewLayoutDefaults
=
(
size
=
videoSize
)
=>
size
===
'portrait'
?
{
x
:
50
,
y
:
50
,
scale
:
1
}
:
{
x
:
50
,
y
:
50
,
scale
:
1
}
const
[
previewLayout
,
setPreviewLayout
]
=
useState
(()
=>
previewLayoutDefaults
())
const
previewSize
=
videoSizes
[
outputVideoSize
]
const
previewFigureSize
=
getFigureLayerSize
(
previewSize
,
selectedFigure
,
previewImageAspect
)
const
previewDhParams
=
buildPreviewDhParams
(
previewSize
,
previewLayout
,
selectedFigure
,
previewImageAspect
,
)
const
previewLayerStyle
=
{
width
:
`
${(
previewFigureSize
.
width
/
previewSize
.
width
)
*
100
}
%`
,
height
:
`
${(
previewFigureSize
.
height
/
previewSize
.
height
)
*
100
}
%`
,
left
:
`
${
previewLayout
.
x
}
%`
,
top
:
`
${
previewLayout
.
y
}
%`
,
transform
:
`translate(-50%, -50%) scale(
${
previewLayout
.
scale
}
)`
,
}
const
generatedPreviewMatches
=
!!
generatedVideo
&&
(
selectedHistoryTaskId
===
generatedVideo
.
taskId
||
!
generatedVideo
.
options
||
(
generatedVideo
.
options
.
type
===
type
&&
(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
&&
fissionFigureIds
.
length
?
fissionFigureIds
.
map
(
String
).
includes
(
String
(
generatedVideo
.
options
.
figureId
))
:
String
(
generatedVideo
.
options
.
figureId
)
===
String
(
selectedFigure
?.
id
))
&&
generatedVideo
.
options
.
driveType
===
'TEXT'
&&
generatedVideo
.
options
.
text
===
textValue
.
trim
()
&&
(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
&&
fissionVoiceIds
.
length
?
fissionVoiceIds
.
map
(
String
).
includes
(
String
(
generatedVideo
.
options
.
voiceId
))
:
String
(
generatedVideo
.
options
.
voiceId
)
===
String
(
selectedVoice
?.
id
))
&&
Number
(
generatedVideo
.
options
.
speed
)
===
speed
&&
Number
(
generatedVideo
.
options
.
volume
)
===
volume
&&
Number
(
generatedVideo
.
options
.
pitch
)
===
pitch
&&
!
generatedVideo
.
options
.
transparent
&&
Number
(
generatedVideo
.
options
.
width
)
===
previewSize
.
width
&&
Number
(
generatedVideo
.
options
.
height
)
===
previewSize
.
height
&&
(
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
(
generatedVideo
.
options
.
model
||
DEFAULT_MODEL
)
===
model
&&
generatedVideo
.
options
.
templateId
===
templateId
&&
(
generatedVideo
.
options
.
title
||
''
)
===
title
&&
(
generatedVideo
.
options
.
logoUrl
||
''
)
===
logoUrl
&&
(
generatedVideo
.
options
.
bgmUrl
||
''
)
===
bgmUrl
&&
(
generatedVideo
.
options
.
materialUrl
||
''
)
===
materialUrl
&&
(
generatedVideo
.
options
.
extendBackgroundUrl
||
''
)
===
extendBackgroundUrl
&&
(
generatedVideo
.
options
.
riskTip
||
''
)
===
riskTip
&&
(
generatedVideo
.
options
.
openingFileId
||
''
)
===
(
openingMaterial
?.
fileId
||
''
)
&&
(
generatedVideo
.
options
.
endingFileId
||
''
)
===
(
endingMaterial
?.
fileId
||
''
)
&&
JSON
.
stringify
(
generatedVideo
.
options
.
mashupMaterials
||
[])
===
JSON
.
stringify
(
mashupMaterials
)
&&
JSON
.
stringify
(
generatedVideo
.
options
.
fissionFigureIds
||
[])
===
JSON
.
stringify
(
fissionFigureIds
)
&&
JSON
.
stringify
(
generatedVideo
.
options
.
fissionVoiceIds
||
[])
===
JSON
.
stringify
(
fissionVoiceIds
)
:
(
generatedVideo
.
options
.
language
||
'auto'
)
===
language
&&
(
generatedVideo
.
options
.
backgroundImageUrl
||
''
)
===
backgroundImageUrl
&&
JSON
.
stringify
(
generatedVideo
.
options
.
dhParams
)
===
JSON
.
stringify
(
previewDhParams
))))
const
generatedVideoWidth
=
Number
(
generatedVideo
?.
options
?.
width
)
const
generatedVideoHeight
=
Number
(
generatedVideo
?.
options
?.
height
)
const
generatedVideoSize
=
generatedVideoWidth
&&
generatedVideoHeight
?
generatedVideoWidth
>=
generatedVideoHeight
?
'landscape'
:
'portrait'
:
outputVideoSize
const
previewStageSize
=
showGeneratedPreview
&&
generatedPreviewMatches
&&
generatedVideo
?.
options
?
generatedVideoSize
:
outputVideoSize
useEffect
(()
=>
{
if
(
!
selectedFigure
&&
figures
.
length
>
0
)
setSelectedFigure
(
figures
[
0
])
if
(
selectedFigure
)
{
const
latestFigure
=
figures
.
find
((
figure
)
=>
String
(
figure
.
id
)
===
String
(
selectedFigure
.
id
))
if
(
latestFigure
&&
latestFigure
!==
selectedFigure
)
setSelectedFigure
(
latestFigure
)
}
}
if
(
!
selectedVoice
&&
femaleVoices
.
length
>
0
)
setSelectedVoice
(
femaleVoices
[
0
])
},
[
figures
,
femaleVoices
,
selectedFigure
,
selectedVoice
])
},
[
figures
,
femaleVoices
,
selectedFigure
,
selectedVoice
])
useEffect
(()
=>
{
if
(
type
===
DIGITAL_HUMAN_TYPES
.
BASIC
&&
figureVideoSize
)
setVideoSize
(
figureVideoSize
)
},
[
figureVideoSize
,
selectedFigure
?.
id
,
type
])
useEffect
(()
=>
{
setPreviewLayout
(
previewLayoutDefaults
(
outputVideoSize
))
},
[
selectedFigure
?.
id
,
outputVideoSize
])
useEffect
(()
=>
{
setPreviewImageAspect
(
null
)
},
[
selectedFigure
?.
id
])
useEffect
(()
=>
{
setShowGeneratedPreview
(
!!
generatedVideo
)
},
[
generatedVideo
])
useEffect
(()
=>
{
setSelectedHistoryTaskId
(
null
)
},
[
type
,
selectedFigure
?.
id
,
selectedVoice
?.
id
,
textValue
,
language
,
speed
,
volume
,
pitch
,
videoSize
,
templateId
,
model
,
title
,
logoUrl
,
bgmUrl
,
backgroundImageUrl
,
materialUrl
,
extendBackgroundUrl
,
riskTip
,
openingMaterial
,
endingMaterial
,
mashupMaterials
,
fissionFigureIds
,
fissionVoiceIds
,
previewLayout
,
])
const
handleVoicePlay
=
(
voice
)
=>
{
const
handleVoicePlay
=
(
voice
)
=>
{
if
(
!
voice
||
!
voice
.
previewUrl
||
!
audioRef
.
current
)
return
const
audio
=
audioRef
.
current
if
(
playingVoiceId
===
voice
.
id
)
{
if
(
playingVoiceId
===
voice
.
id
)
{
audioRef
.
current
.
pause
()
audio
.
pause
()
setPlayingVoiceId
(
null
)
return
}
audio
.
src
=
voice
.
previewUrl
audio
.
play
().
catch
(()
=>
{
setPlayingVoiceId
(
null
)
setPlayingVoiceId
(
null
)
}
else
{
message
.
error
(
'音色试听失败'
)
audioRef
.
current
.
src
=
voice
.
previewUrl
})
audioRef
.
current
.
play
()
setPlayingVoiceId
(
voice
.
id
)
setPlayingVoiceId
(
voice
.
id
)
audioRef
.
current
.
onended
=
()
=>
setPlayingVoiceId
(
null
)
}
const
handleVoiceSelect
=
(
voice
)
=>
{
setSelectedVoice
(
voice
)
setVoicePickerOpen
(
false
)
}
const
renderVoiceGrid
=
(
voices
)
=>
{
return
(
<>
<
div
className=
'voice-grid'
>
{
voices
.
map
((
voice
)
=>
(
<
div
role=
'button'
tabIndex=
{
0
}
key=
{
voice
.
id
}
className=
{
`voice-item ${selectedVoice?.id === voice.id ? 'active' : ''}`
}
onClick=
{
()
=>
handleVoiceSelect
(
voice
)
}
onKeyDown=
{
(
event
)
=>
{
if
(
event
.
key
===
'Enter'
||
event
.
key
===
' '
)
{
event
.
preventDefault
()
handleVoiceSelect
(
voice
)
}
}
}
>
<
span
className=
'voice-picker-avatar'
>
<
UserOutlined
/>
</
span
>
<
span
className=
'v-info'
>
<
strong
>
{
voice
.
name
}
</
strong
>
<
small
>
{
voice
.
style
}
</
small
>
</
span
>
<
Button
type=
'text'
shape=
'circle'
disabled=
{
!
voice
.
previewUrl
}
aria
-
label=
{
voice
.
previewUrl
?
`试听${voice.name}`
:
`${voice.name}暂无试听`
}
icon=
{
playingVoiceId
===
voice
.
id
?
<
PauseCircleOutlined
/>
:
<
PlayCircleOutlined
/>
}
onClick=
{
(
event
)
=>
{
event
.
stopPropagation
()
handleVoicePlay
(
voice
)
}
}
/>
</
div
>
))
}
</
div
>
</>
)
}
const
renderVoiceSlider
=
(
label
,
value
,
onChange
,
low
,
high
)
=>
(
<
div
className=
'range-setting'
>
<
div
className=
'range-label'
>
{
label
}
</
div
>
<
div
className=
'range-control'
>
<
span
className=
'range-edge'
>
{
low
}
</
span
>
<
Slider
min=
{
0
}
max=
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
15
:
10
}
value=
{
value
}
onChange=
{
onChange
}
tooltip=
{
{
open
:
false
}
}
/>
<
span
className=
'range-edge'
>
{
high
}
</
span
>
<
InputNumber
min=
{
0
}
max=
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
15
:
10
}
value=
{
value
}
onChange=
{
(
next
)
=>
onChange
(
next
??
0
)
}
/>
</
div
>
</
div
>
)
const
handlePreviewPointerDown
=
(
event
,
mode
=
'move'
)
=>
{
setPreviewFigureSelected
(
true
)
const
figure
=
event
.
currentTarget
.
closest
(
'.preview-layer'
)
||
event
.
currentTarget
const
figureRect
=
figure
.
getBoundingClientRect
()
figure
.
setPointerCapture
(
event
.
pointerId
)
previewDragRef
.
current
=
{
pointerId
:
event
.
pointerId
,
mode
,
startX
:
event
.
clientX
,
startY
:
event
.
clientY
,
layout
:
previewLayout
,
layerWidthPx
:
figureRect
.
width
,
layerHeightPx
:
figureRect
.
height
,
}
}
const
handlePreviewPointerMove
=
(
event
)
=>
{
const
drag
=
previewDragRef
.
current
if
(
!
drag
||
drag
.
pointerId
!==
event
.
pointerId
)
return
const
stage
=
event
.
currentTarget
.
closest
(
'.preview-stage'
)
if
(
!
stage
)
return
const
rect
=
stage
.
getBoundingClientRect
()
if
(
drag
.
mode
!==
'move'
)
{
// 拖动对角时锚定对角的另一条边:宽度/高度变化的一半平移到中心点
const
horizontalDirection
=
drag
.
mode
.
endsWith
(
'e'
)
?
1
:
-
1
const
verticalDirection
=
drag
.
mode
.
startsWith
(
's'
)
?
1
:
-
1
const
widthDelta
=
(
event
.
clientX
-
drag
.
startX
)
*
horizontalDirection
const
heightDelta
=
(
event
.
clientY
-
drag
.
startY
)
*
verticalDirection
const
widthScaleDelta
=
widthDelta
/
Math
.
max
(
1
,
drag
.
layerWidthPx
)
const
heightScaleDelta
=
heightDelta
/
Math
.
max
(
1
,
drag
.
layerHeightPx
)
const
scaleDelta
=
Math
.
abs
(
widthScaleDelta
)
>=
Math
.
abs
(
heightScaleDelta
)
?
widthScaleDelta
:
heightScaleDelta
const
scale
=
Math
.
max
(
0.3
,
Math
.
min
(
1.5
,
drag
.
layout
.
scale
*
(
1
+
scaleDelta
)))
const
scaleRatio
=
scale
/
drag
.
layout
.
scale
const
resizedWidthDelta
=
drag
.
layerWidthPx
*
(
scaleRatio
-
1
)
const
resizedHeightDelta
=
drag
.
layerHeightPx
*
(
scaleRatio
-
1
)
setPreviewLayout
({
...
drag
.
layout
,
scale
,
x
:
drag
.
layout
.
x
+
((
horizontalDirection
*
resizedWidthDelta
)
/
2
/
rect
.
width
)
*
100
,
y
:
drag
.
layout
.
y
+
((
verticalDirection
*
resizedHeightDelta
)
/
2
/
rect
.
height
)
*
100
,
})
return
}
setPreviewLayout
({
...
drag
.
layout
,
x
:
drag
.
layout
.
x
+
((
event
.
clientX
-
drag
.
startX
)
/
rect
.
width
)
*
100
,
y
:
drag
.
layout
.
y
+
((
event
.
clientY
-
drag
.
startY
)
/
rect
.
height
)
*
100
,
})
}
const
handlePreviewPointerUp
=
(
event
)
=>
{
if
(
previewDragRef
.
current
?.
pointerId
===
event
.
pointerId
)
{
previewDragRef
.
current
=
null
}
}
const
handleResetPreview
=
()
=>
{
setPreviewLayout
(
previewLayoutDefaults
(
outputVideoSize
))
setShowGeneratedPreview
(
false
)
setPreviewFigureSelected
(
false
)
setSelectedHistoryTaskId
(
null
)
}
const
handleWorkspaceChange
=
(
nextMode
)
=>
{
setActiveMode
(
nextMode
)
setShowGeneratedPreview
(
false
)
setPreviewFigureSelected
(
false
)
setSelectedHistoryTaskId
(
null
)
if
(
nextMode
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
)
{
setVideoSize
(
selectedTemplate
?.
ratio
||
'portrait'
)
}
else
if
(
nextMode
===
DIGITAL_HUMAN_TYPES
.
BASIC
)
{
setVideoSize
(
figureVideoSize
||
'landscape'
)
setSpeed
((
current
)
=>
Math
.
min
(
current
,
10
))
setVolume
((
current
)
=>
Math
.
min
(
current
,
10
))
setPitch
((
current
)
=>
Math
.
min
(
current
,
10
))
}
}
}
}
const
handleGenerate
=
async
()
=>
{
const
handleGenerate
=
async
()
=>
{
if
(
!
canGenerate
)
return
message
.
warning
(
`请先完成:
${
nextRequiredItem
}
`
)
if
(
!
selectedFigure
)
return
message
.
warning
(
'请选择数字人形象'
)
if
(
!
selectedFigure
)
return
message
.
warning
(
'请选择数字人形象'
)
if
(
!
textValue
.
trim
())
return
message
.
warning
(
'请输入播报文本'
)
if
(
!
textValue
.
trim
())
return
message
.
warning
(
'请输入播报文本'
)
if
(
!
selectedVoice
)
return
message
.
warning
(
'请选择音色'
)
if
(
!
selectedVoice
)
return
message
.
warning
(
'请选择音色'
)
try
{
try
{
setSelectedHistoryTaskId
(
null
)
const
size
=
videoSizes
[
outputVideoSize
]
await
generateVideo
({
await
generateVideo
({
type
,
figureId
:
selectedFigure
.
id
,
figureId
:
selectedFigure
.
id
,
figureName
:
selectedFigure
.
name
,
figureName
:
selectedFigure
.
name
,
driveType
:
'TEXT'
,
driveType
:
'TEXT'
,
text
:
textValue
.
trim
(),
text
:
textValue
.
trim
(),
voiceId
:
selectedVoice
?.
id
,
voiceId
:
selectedVoice
?.
id
,
width
:
720
,
speed
,
height
:
1280
,
volume
,
pitch
,
language
:
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
||
language
===
'auto'
?
undefined
:
language
,
width
:
size
.
width
,
height
:
size
.
height
,
transparent
:
false
,
// 预览拖拽出的位置只对基础接口生效,模板样式由模板自身控制布局
dhParams
:
type
===
DIGITAL_HUMAN_TYPES
.
BASIC
?
previewDhParams
:
undefined
,
enableSubtitle
:
false
,
model
,
templateId
,
title
,
logoUrl
,
bgmUrl
,
backgroundImageUrl
,
materialUrl
,
extendBackgroundUrl
,
riskTip
,
openingFileId
:
openingMaterial
?.
fileId
,
endingFileId
:
endingMaterial
?.
fileId
,
mashupMaterials
,
fissionFigureIds
,
fissionVoiceIds
,
})
})
}
catch
(
error
)
{}
}
catch
{}
}
const
handleRetry
=
async
()
=>
{
try
{
await
retryTask
()
}
catch
(
retryError
)
{
message
.
error
(
retryError
.
message
||
'重新提交失败'
)
}
}
}
const
handleInsert
=
async
()
=>
{
const
handleInsert
=
async
()
=>
{
if
(
!
generatedVideo
)
return
if
(
!
generatedVideo
)
return
setIsInserting
(
true
)
try
{
try
{
message
.
loading
({
content
:
'处理中...'
,
key
:
'inserting'
})
message
.
loading
({
content
:
'处理中...'
,
key
:
'inserting'
})
const
ossUrl
=
await
uploadFileByUrl
(
generatedVideo
.
url
)
const
ossUrl
=
await
uploadFileByUrl
(
generatedVideo
.
url
)
...
@@ -112,219 +599,670 @@ export default function AIDigitalHumanModal(props) {
...
@@ -112,219 +599,670 @@ export default function AIDigitalHumanModal(props) {
message
.
success
({
content
:
'已插入文章'
,
key
:
'inserting'
})
message
.
success
({
content
:
'已插入文章'
,
key
:
'inserting'
})
setIsModalOpen
(
false
)
setIsModalOpen
(
false
)
}
}
}
catch
(
error
)
{
}
catch
{
message
.
error
({
content
:
'插入失败'
,
key
:
'inserting'
})
message
.
error
({
content
:
'插入失败'
,
key
:
'inserting'
})
}
finally
{
setIsInserting
(
false
)
}
}
}
}
const
handleClose
=
()
=>
{
audioRef
.
current
?.
pause
()
setIsModalOpen
(
false
)
}
return
(
return
(
<
Modal
<
Modal
title=
'数字人 AI 视频生成'
title=
{
<
div
className=
'digital-human-modal-title'
>
<
span
>
数字人
</
span
>
<
small
>
视频合成与形象定制
</
small
>
</
div
>
}
open=
{
isModalOpen
}
open=
{
isModalOpen
}
onCancel=
{
()
=>
setIsModalOpen
(
false
)
}
onCancel=
{
handleClose
}
footer=
{
null
}
footer=
{
null
}
width=
{
1000
}
width=
{
1600
}
className=
'digital-human-modal'
centered
centered
destroyOnHidden
destroyOnHidden
>
>
<
audio
ref=
{
audioRef
}
onEnded=
{
()
=>
setPlayingVoiceId
(
null
)
}
/>
<
div
className=
'ai-digital-human'
>
<
div
className=
'ai-digital-human'
>
<
Row
gutter=
{
[
24
,
16
]
}
>
<
Tabs
<
Col
span=
{
24
}
>
className=
'digital-human-workspace-tabs'
{
/* 形象选择 */
}
activeKey=
{
activeMode
}
<
div
className=
'section-box'
>
items=
{
workspaceTabs
}
<
div
className=
'section-title'
>
1. 选择数字人形象
</
div
>
onChange=
{
handleWorkspaceChange
}
{
loadingFigures
&&
figures
.
length
===
0
?
(
<
div
style=
{
{
textAlign
:
'center'
,
padding
:
'20px'
}
}
>
<
Spin
tip=
'同步云端形象中...'
/>
</
div
>
)
:
figures
.
length
>
0
?
(
<
div
className=
'figure-list'
>
{
figures
.
map
((
fig
)
=>
(
<
div
key=
{
fig
.
id
}
className=
{
`figure-item ${selectedFigure?.id === fig.id ? 'active' : ''}`
}
onClick=
{
()
=>
setSelectedFigure
(
fig
)
}
>
<
div
className=
'avatar-wrapper'
>
{
fig
.
avatar
?
(
<
img
src=
{
fig
.
avatar
}
className=
'figure-avatar'
onError=
{
(
e
)
=>
{
e
.
target
.
style
.
display
=
'none'
e
.
target
.
nextSibling
.
style
.
display
=
'flex'
}
}
/>
/>
{
activeMode
===
CUSTOMIZE_WORKSPACE
?
(
<
DigitalHumanCustomizePanel
onSuccess=
{
refreshFigures
}
/>
)
:
null
}
)
:
null
}
<
div
<
div
className=
'avatar-fallback'
className=
{
`component-layout ${activeMode === CUSTOMIZE_WORKSPACE ? 'is-hidden' : ''}`
}
style=
{
{
display
:
fig
.
avatar
?
'none'
:
'flex'
}
}
>
>
<
UserOutlined
/>
<
div
className=
'config-column'
>
</
div
>
<
section
className=
'config-section figure-section'
>
</
div
>
<
h2
>
形象
</
h2
>
<
div
className=
'figure-name'
>
{
fig
.
name
}
</
div
>
<
button
</
div
>
type=
'button'
))
}
className=
'selected-media-card'
</
div
>
onClick=
{
()
=>
setFigurePickerOpen
(
true
)
}
disabled=
{
loadingFigures
&&
figures
.
length
===
0
}
>
<
span
className=
'selected-media-thumb'
>
{
selectedFigure
?.
avatar
?
(
<
img
src=
{
selectedFigure
.
avatar
}
alt=
{
selectedFigure
.
name
}
/>
)
:
(
)
:
(
<
Empty
description=
'暂无可用形象'
/>
<
UserOutlined
/>
)
}
)
}
</
div
>
</
span
>
<
span
className=
'selected-media-copy'
>
<
strong
>
{
selectedFigure
?.
name
||
'请选择形象'
}
</
strong
>
<
span
>
ID:
{
selectedFigure
?.
id
||
'--'
}
<
CopyOutlined
/>
</
span
>
</
span
>
<
SwapOutlined
className=
'selected-media-action'
/>
</
button
>
</
section
>
{
/* 驱动配置 */
}
<
Collapse
<
div
className=
'section-box'
>
ghost
<
div
className=
'section-title'
>
2. 配置播报内容与音色
</
div
>
expandIconPosition=
'end'
<
div
className=
'drive-content'
>
defaultActiveKey=
{
[
'voice'
]
}
className=
'config-collapse'
items=
{
[
{
key
:
'voice'
,
label
:
'声音输出设置'
,
children
:
(
<
div
className=
'voice-output-settings'
>
<
div
className=
'field-label'
>
声音
</
div
>
<
button
type=
'button'
className=
'selected-media-card voice-card'
onClick=
{
()
=>
setVoicePickerOpen
(
true
)
}
>
<
span
className=
'selected-media-thumb voice-avatar'
>
<
UserOutlined
/>
</
span
>
<
span
className=
'selected-media-copy'
>
<
strong
>
{
selectedVoice
?.
name
||
'请选择声音'
}
</
strong
>
<
span
>
ID:
{
selectedVoice
?.
id
||
'--'
}
<
CopyOutlined
/>
</
span
>
<
span
className=
'voice-tags'
>
<
em
>
{
selectedVoice
?.
gender
||
'女声'
}
</
em
>
<
em
>
中文、方言
</
em
>
{
selectedVoice
?.
style
?
<
em
>
{
selectedVoice
.
style
}
</
em
>
:
null
}
</
span
>
</
span
>
<
SwapOutlined
className=
'selected-media-action'
/>
</
button
>
<
div
className=
'field-label'
>
语言
</
div
>
<
Select
className=
'full-control'
value=
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
'auto'
:
language
}
onChange=
{
setLanguage
}
disabled=
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
}
options=
{
[
{
label
:
'自动'
,
value
:
'auto'
},
{
label
:
'中文'
,
value
:
'Chinese'
},
{
label
:
'方言'
,
value
:
'Chinese,Yue'
},
]
}
/>
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
(
<
div
className=
'field-hint'
>
高级视频模板暂不支持多语言。
</
div
>
)
:
null
}
{
renderVoiceSlider
(
'语速'
,
speed
,
setSpeed
,
'慢'
,
'快'
)
}
{
renderVoiceSlider
(
'音量'
,
volume
,
setVolume
,
'小'
,
'大'
)
}
{
renderVoiceSlider
(
'语调'
,
pitch
,
setPitch
,
'低'
,
'高'
)
}
<
section
className=
'script-section'
>
<
div
className=
'field-label'
>
播报文案
</
div
>
<
div
className=
'script-box'
>
<
TextArea
<
TextArea
placeholder=
'请输入要播报的文本内容...'
bordered=
{
false
}
rows=
{
4
}
placeholder=
'请输入数字人要播报的内容'
maxLength=
{
2000
}
rows=
{
7
}
maxLength=
{
maxTextLength
}
showCount
showCount
value=
{
textValue
}
value=
{
textValue
}
onChange=
{
(
e
)
=>
setTextValue
(
e
.
target
.
value
)
}
onChange=
{
(
event
)
=>
setTextValue
(
event
.
target
.
value
)
}
style=
{
{
marginBottom
:
12
}
}
/>
/>
<
div
className=
'voice-selection'
>
</
div
>
<
div
className=
'sub-title'
>
精选配音:
</
div
>
</
section
>
<
Tabs
</
div
>
size=
'small'
),
},
]
}
/>
<
Collapse
ghost
expandIconPosition=
'end'
defaultActiveKey=
{
[
'base'
]
}
className=
'config-collapse output-collapse'
items=
{
[
items=
{
[
{
{
key
:
'femal
e'
,
key
:
'bas
e'
,
label
:
'女声
'
,
label
:
'基础输出设置
'
,
children
:
(
children
:
(
<
div
className=
'voice-grid'
>
<
div
className=
'output-settings'
>
{
femaleVoices
.
map
((
v
)
=>
(
<
div
className=
'field-label'
>
输出画面
</
div
>
<
div
<
div
className=
'choice-grid'
>
key=
{
v
.
id
}
{
[
'landscape'
,
'portrait'
].
map
((
size
)
=>
(
className=
{
`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`
}
<
button
onClick=
{
()
=>
setSelectedVoice
(
v
)
}
type=
'button'
key=
{
size
}
className=
{
outputVideoSize
===
size
?
'active'
:
''
}
onClick=
{
()
=>
setVideoSize
(
size
)
}
>
>
<
div
className=
'v-info'
>
<
span
className=
{
`ratio-icon ${size}`
}
/>
<
span
className=
'v-name'
>
{
v
.
name
}
</
span
>
<
span
>
{
size
===
'landscape'
?
'16:9'
:
'9:16'
}
</
span
>
<
span
className=
'v-style'
>
{
v
.
style
}
</
span
>
<
small
>
</
div
>
{
videoSizes
[
size
].
width
}
*
{
videoSizes
[
size
].
height
}
<
Button
</
small
>
type=
'text'
</
button
>
shape=
'circle'
icon=
{
playingVoiceId
===
v
.
id
?
(
<
PauseCircleOutlined
/>
)
:
(
<
PlayCircleOutlined
/>
)
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
handleVoicePlay
(
v
)
}
}
/>
</
div
>
))
}
))
}
</
div
>
</
div
>
</
div
>
),
),
},
},
{
{
key
:
'male
'
,
key
:
'advanced
'
,
label
:
'男声
'
,
label
:
'高级输出设置
'
,
children
:
(
children
:
(
<
div
className=
'voice-grid'
>
<
div
className=
'output-settings'
>
{
maleVoices
.
map
((
v
)
=>
(
{
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
?
(
<
div
<>
key=
{
v
.
id
}
<
div
className=
'field-label'
>
视频样式
</
div
>
className=
{
`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`
}
<
div
className=
'template-grid'
>
onClick=
{
()
=>
setSelectedVoice
(
v
)
}
{
ADVANCED_TEMPLATES
.
map
((
template
)
=>
(
<
button
type=
'button'
key=
{
template
.
value
}
className=
{
`template-card ${templateId === template.value ? 'active' : ''}`
}
onClick=
{
()
=>
{
setTemplateId
(
template
.
value
)
setVideoSize
(
template
.
ratio
)
}
}
>
>
<
div
className=
'v-info'
>
<
span
className=
{
`template-preview ${template.ratio}`
}
>
<
span
className=
'v-name'
>
{
v
.
name
}
</
span
>
<
img
src=
{
template
.
preview
}
alt=
{
`${template.label}效果预览`
}
/>
<
span
className=
'v-style'
>
{
v
.
style
}
</
span
>
</
span
>
<
span
className=
'template-meta'
>
<
strong
>
{
template
.
label
}
</
strong
>
<
small
>
{
template
.
ratio
===
'landscape'
?
'横屏'
:
'竖屏'
}
</
small
>
</
span
>
{
templateId
===
template
.
value
?
(
<
CheckOutlined
className=
'template-selected'
/>
)
:
null
}
</
button
>
))
}
</
div
>
</
div
>
<
Button
<
div
className=
'advanced-field'
>
type=
'text'
<
div
className=
'field-label'
>
生成模型
</
div
>
shape=
'circle'
<
Select
icon=
{
className=
'full-control'
playingVoiceId
===
v
.
id
?
(
value=
{
model
}
<
PauseCircleOutlined
/>
onChange=
{
setModel
}
)
:
(
options=
{
[
<
PlayCircleOutlined
/>
{
label
:
'高质量(quality_v2)'
,
value
:
'quality_v2'
},
)
{
label
:
'快速(turbo_v2)'
,
value
:
'turbo_v2'
},
}
]
}
onClick=
{
(
e
)
=>
{
/>
e
.
stopPropagation
()
</
div
>
handleVoicePlay
(
v
)
<
div
className=
'advanced-field'
>
<
div
className=
'field-label'
>
视频标题
</
div
>
<
Input
maxLength=
{
30
}
showCount
value=
{
title
}
onChange=
{
(
event
)
=>
setTitle
(
event
.
target
.
value
)
}
placeholder=
'可选,最多 30 个字符'
/>
</
div
>
<
DigitalHumanUploadField
label=
'Logo(可选)'
value=
{
logoUrl
}
onChange=
{
setLogoUrl
}
accept=
'.png,.jpg,.jpeg,.bmp'
maxSizeMB=
{
3
}
filenamePrefix=
'digital-human-logo'
previewType=
'image'
uploadHint=
'PNG、JPG、JPEG、BMP,不超过 3MB'
/>
<
DigitalHumanUploadField
label=
'背景音乐(可选)'
value=
{
bgmUrl
}
onChange=
{
setBgmUrl
}
accept=
'.wav,.mp3,.wma,.m4a'
maxSizeMB=
{
120
}
filenamePrefix=
'digital-human-bgm'
hint=
'支持 WAV、MP3、WMA、M4A,不超过 120MB。'
/>
<
DigitalHumanUploadField
label=
'模板背景素材(可选)'
value=
{
materialUrl
}
onChange=
{
setMaterialUrl
}
accept=
'.png,.jpg,.jpeg,.bmp,.mp4'
maxImageSizeMB=
{
3
}
maxVideoSizeMB=
{
500
}
filenamePrefix=
'digital-human-material'
hint=
'图片不超过 3MB,MP4 视频不超过 500MB。'
/>
<
DigitalHumanUploadField
label=
'扩展自定义背景(可选)'
value=
{
extendBackgroundUrl
}
onChange=
{
setExtendBackgroundUrl
}
accept=
'.png,.jpg,.jpeg,.bmp,.mp4'
maxImageSizeMB=
{
3
}
maxVideoSizeMB=
{
500
}
filenamePrefix=
'digital-human-extend-background'
hint=
'横屏输出使用横屏扩展背景、竖屏输出使用竖屏扩展背景;模板和模板背景素材比例与输出相反。'
/>
<
div
className=
'advanced-field'
>
<
div
className=
'field-label'
>
水印描述(可选)
</
div
>
<
Input
maxLength=
{
30
}
showCount
value=
{
riskTip
}
onChange=
{
(
event
)
=>
setRiskTip
(
event
.
target
.
value
)
}
placeholder=
'默认显示在视频底部居中'
/>
</
div
>
<
DigitalHumanBaiduUploadField
label=
'片头视频'
value=
{
openingMaterial
}
onChange=
{
setOpeningMaterial
}
accept=
'.mp4,video/mp4'
hint=
'可选,仅支持 MP4,不超过 500MB。'
/>
<
DigitalHumanBaiduUploadField
label=
'片尾视频'
value=
{
endingMaterial
}
onChange=
{
setEndingMaterial
}
accept=
'.mp4,video/mp4'
hint=
'可选,仅支持 MP4,不超过 500MB。'
/>
<
DigitalHumanBaiduUploadField
label=
'混剪素材'
value=
{
mashupMaterials
}
onChange=
{
setMashupMaterials
}
accept=
'.png,.jpg,.jpeg,.bmp,.mp4'
multiple
maxCount=
{
10
}
hint=
'可选,最多 10 个;图片不超过 3MB,MP4 视频不超过 500MB。'
/>
<
div
className=
'advanced-field'
>
<
div
className=
'field-label'
>
裂变形象(可选)
</
div
>
<
Select
className=
'full-control'
mode=
'multiple'
maxCount=
{
10
}
allowClear
value=
{
fissionFigureIds
}
onChange=
{
(
values
)
=>
{
setFissionFigureIds
(
values
)
if
(
!
values
.
length
)
setFissionVoiceIds
([])
}
}
}
}
placeholder=
'最多选择 10 个形象'
options=
{
figures
.
map
((
figure
)
=>
({
label
:
`${figure.name}(${figure.id})`
,
value
:
String
(
figure
.
id
),
}))
}
/>
/>
<
div
className=
'field-hint'
>
选择后会覆盖当前形象并批量生成视频。
</
div
>
</
div
>
</
div
>
))
}
<
div
className=
'advanced-field'
>
<
div
className=
'field-label'
>
裂变音色(可选)
</
div
>
<
Select
className=
'full-control'
mode=
'multiple'
maxCount=
{
10
}
allowClear
disabled=
{
!
fissionFigureIds
.
length
}
value=
{
fissionVoiceIds
}
onChange=
{
setFissionVoiceIds
}
placeholder=
'先选择裂变形象,最多选择 10 个音色'
options=
{
allVoices
.
map
((
voice
)
=>
({
label
:
`${voice.name}(${voice.id})`
,
value
:
String
(
voice
.
id
),
}))
}
/>
<
div
className=
'field-hint'
>
不选择时,仅按形象裂变。
</
div
>
</
div
>
</>
)
:
null
}
{
type
===
DIGITAL_HUMAN_TYPES
.
BASIC
?
(
<
DigitalHumanUploadField
label=
'背景图片(可选)'
value=
{
backgroundImageUrl
}
onChange=
{
setBackgroundImageUrl
}
accept=
'image/*'
maxSizeMB=
{
3
}
filenamePrefix=
'digital-human-background'
previewType=
'image'
hint=
'不上传也可以直接生成视频。'
/>
)
:
null
}
</
div
>
</
div
>
),
),
},
},
]
}
]
}
/>
/>
</
div
>
</
div
>
</
div
>
{
/* 已彻底移除背景图片 URL 相关输入 */
}
<
section
className=
'preview-panel'
>
<
div
className=
'preview-panel-heading'
>
<
div
>
<
strong
>
展示调整
</
strong
>
<
span
>
支持拖动调整数字人位置和大小
</
span
>
</
div
>
</
div
>
</
Col
>
<
Button
type=
'text'
icon=
{
<
ReloadOutlined
/>
}
onClick=
{
handleResetPreview
}
>
还原
<
Col
span=
{
24
}
>
</
Button
>
<
div
className=
'preview-panel'
>
</
div
>
<
div
className=
'section-title'
>
生成状态
</
div
>
<
section
className=
'generation-result'
>
<
div
className=
'results-container'
>
{
activeVideoTask
?.
status
?
(
<
div
className=
'generation-result-heading'
>
<
strong
>
生成状态
</
strong
>
<
Tag
color=
{
taskStatus
[
activeVideoTask
.
status
]?.
color
}
>
{
taskStatus
[
activeVideoTask
.
status
]?.
text
||
activeVideoTask
.
status
}
</
Tag
>
</
div
>
)
:
null
}
{
activeVideoTask
&&
error
?
(
<
Alert
type=
'warning'
showIcon
message=
{
error
}
action=
{
activeVideoTask
.
status
===
'PAUSED'
?
(
<
Button
size=
'small'
icon=
{
<
ReloadOutlined
/>
}
onClick=
{
()
=>
resumeTask
()
}
>
继续查询
</
Button
>
)
:
activeVideoTask
.
status
===
'FAILED'
?
(
<
Button
size=
'small'
icon=
{
<
ReloadOutlined
/>
}
onClick=
{
handleRetry
}
>
重新生成
</
Button
>
)
:
null
}
/>
)
:
null
}
<
div
className=
{
`preview-stage ${previewStageSize}`
}
onPointerDown=
{
(
event
)
=>
{
if
(
event
.
target
===
event
.
currentTarget
)
setPreviewFigureSelected
(
false
)
}
}
style=
{
{
backgroundImage
:
type
===
DIGITAL_HUMAN_TYPES
.
BASIC
&&
backgroundImageUrl
?
`url(${backgroundImageUrl})`
:
type
===
DIGITAL_HUMAN_TYPES
.
ADVANCED
&&
selectedTemplate
?.
preview
?
`url(${selectedTemplate.preview})`
:
undefined
,
}
}
>
{
showGeneratedPreview
&&
generatedPreviewMatches
?
(
<
div
className=
'generated-preview-clip'
>
<
video
className=
'generated-preview-video'
src=
{
generatedVideo
.
url
}
controls
aria
-
label=
'生成的数字人视频'
/>
</
div
>
)
:
(
<>
<
div
className=
'preview-canvas-clip'
>
<
div
className=
'preview-image-layer'
style=
{
previewLayerStyle
}
>
{
selectedFigure
?.
avatar
?
(
<
img
src=
{
selectedFigure
.
avatar
}
alt=
{
selectedFigure
.
name
}
onLoad=
{
(
event
)
=>
{
const
{
naturalWidth
,
naturalHeight
}
=
event
.
currentTarget
if
(
naturalWidth
&&
naturalHeight
)
{
setPreviewImageAspect
(
naturalHeight
/
naturalWidth
)
}
}
}
/>
)
:
(
<
UserOutlined
/>
)
}
</
div
>
</
div
>
<
div
className=
{
`preview-layer ${previewFigureSelected ? 'selected' : ''}`
}
style=
{
previewLayerStyle
}
onPointerDown=
{
handlePreviewPointerDown
}
onPointerMove=
{
handlePreviewPointerMove
}
onPointerUp=
{
handlePreviewPointerUp
}
onPointerCancel=
{
handlePreviewPointerUp
}
role=
'img'
aria
-
selected=
{
previewFigureSelected
}
aria
-
label=
'可拖动调整数字人视频位置和大小的预览'
>
{
previewFigureSelected
?
[
'nw'
,
'ne'
,
'sw'
,
'se'
].
map
((
corner
)
=>
(
<
span
key=
{
corner
}
className=
{
`preview-resize-handle ${corner}`
}
onPointerDown=
{
(
event
)
=>
{
event
.
stopPropagation
()
handlePreviewPointerDown
(
event
,
corner
)
}
}
/>
))
:
null
}
</
div
>
</>
)
}
{
isGenerating
?
(
{
isGenerating
?
(
<
div
className=
'generating-box'
>
<
div
className=
'generating-box'
>
<
Spin
size=
'large'
/>
<
Spin
/>
<
div
className=
'gen-text'
>
视频正在录制中...
</
div
>
<
span
>
视频正在生成
</
span
>
<
div
className=
'gen-hint'
>
预计 1 分钟左右完成
</
div
>
</
div
>
)
:
null
}
</
div
>
</
div
>
)
:
generatedVideo
?
(
{
generatedVideo
&&
generatedPreviewMatches
?
(
<
div
className=
'final-video-box'
>
<
video
src=
{
generatedVideo
.
url
}
controls
autoPlay
/>
<
div
className=
'video-actions'
>
<
div
className=
'video-actions'
>
<
Button
<
Button
block
size=
'large'
icon=
{
<
DownloadOutlined
/>
}
icon=
{
<
DownloadOutlined
/>
}
onClick=
{
()
=>
saveAs
(
generatedVideo
.
url
,
'ai_video.mp4'
)
}
onClick=
{
()
=>
saveAs
(
generatedVideo
.
url
,
'ai_video.mp4'
)
}
>
>
下载视频
下载视频
</
Button
>
</
Button
>
{
generatedVideo
.
subtitleFileUrl
?
(
<
Button
icon=
{
<
DownloadOutlined
/>
}
onClick=
{
()
=>
saveAs
(
generatedVideo
.
subtitleFileUrl
,
'subtitle.srt'
)
}
>
下载字幕
</
Button
>
)
:
null
}
<
Button
<
Button
block
type=
'primary'
type=
'primary'
size=
'large'
icon=
{
<
CheckOutlined
/>
}
icon=
{
<
CheckOutlined
/>
}
onClick=
{
handleInsert
}
onClick=
{
handleInsert
}
loading=
{
isInserting
}
>
>
插入编辑器
插入文章
</
Button
>
</
Button
>
</
div
>
</
div
>
</
div
>
)
:
null
}
)
:
(
<
div
className=
'empty-preview'
>
<
VideoCameraOutlined
style=
{
{
fontSize
:
64
,
opacity
:
0.1
}
}
/>
<
p
>
等待配置完成后点击开始
</
p
>
</
div
>
)
}
</
div
>
<
Divider
style=
{
{
margin
:
'12px 0'
}
}
/>
<
Button
<
Button
type=
'primary'
type=
'primary'
size=
'large'
block
block
icon=
{
<
VideoCameraOutlined
/>
}
className=
'generation-submit'
onClick=
{
handleGenerate
}
onClick=
{
handleGenerate
}
loading=
{
isGenerating
}
loading=
{
isGenerating
}
className=
'submit-btn'
>
>
开始生成 AI
视频
生成数字人
视频
</
Button
>
</
Button
>
</
section
>
</
section
>
</
div
>
{
activeMode
!==
CUSTOMIZE_WORKSPACE
&&
visibleTasks
.
length
>
0
?
(
<
div
className=
'section-box task-history'
>
<
div
className=
'section-title'
>
最近生成
</
div
>
<
div
className=
'task-list'
>
{
visibleTasks
.
map
((
task
)
=>
(
<
button
type=
'button'
key=
{
task
.
taskId
}
className=
{
`task-item ${activeTask?.taskId === task.taskId ? 'active' : ''}`
}
onClick=
{
()
=>
{
setSelectedHistoryTaskId
(
task
.
status
===
'SUCCESS'
&&
task
.
url
?
task
.
taskId
:
null
,
)
selectTask
(
task
)
}
}
>
<
span
className=
'task-name'
>
{
DIGITAL_HUMAN_TYPE_LABELS
[
task
.
type
]
||
'基础接口'
}
·
{
' '
}
{
task
.
figureName
||
'视频'
}
</
span
>
<
span
className=
'task-time'
>
{
new
Date
(
task
.
createdAt
).
toLocaleString
()
}
</
span
>
<
Tag
color=
{
taskStatus
[
task
.
status
]?.
color
}
>
{
taskStatus
[
task
.
status
]?.
text
||
task
.
status
}
</
Tag
>
</
button
>
))
}
</
div
>
</
div
>
</
div
>
</
Col
>
)
:
null
}
</
Row
>
</
div
>
</
div
>
<
Modal
open=
{
figurePickerOpen
}
title=
'选择形象'
footer=
{
null
}
onCancel=
{
()
=>
setFigurePickerOpen
(
false
)
}
width=
{
920
}
className=
'digital-human-picker-modal figure-picker-modal'
>
{
loadingFigures
&&
figures
.
length
===
0
?
(
<
div
className=
'picker-loading'
>
<
Spin
tip=
'同步云端形象中...'
/>
</
div
>
)
:
figures
.
length
>
0
?
(
<>
<
Space
wrap
size=
'middle'
>
<
Select
value=
{
figureFilters
.
gender
}
popupMatchSelectWidth=
{
false
}
options=
{
[
{
label
:
'全部性别'
,
value
:
'all'
},
{
label
:
'女性'
,
value
:
'female'
},
{
label
:
'男性'
,
value
:
'male'
},
]
}
onChange=
{
(
value
)
=>
setFigureFilters
((
current
)
=>
({
...
current
,
gender
:
value
}))
}
/>
<
Select
value=
{
figureFilters
.
style
}
popupMatchSelectWidth=
{
false
}
options=
{
[
{
label
:
'全部风格'
,
value
:
'all'
},
...
figureFilterOptions
.
styles
.
map
((
style
)
=>
({
label
:
style
,
value
:
style
})),
]
}
onChange=
{
(
value
)
=>
setFigureFilters
((
current
)
=>
({
...
current
,
style
:
value
}))
}
/>
<
Select
value=
{
figureFilters
.
pose
}
popupMatchSelectWidth=
{
false
}
options=
{
[
{
label
:
'全部姿态&机位'
,
value
:
'all'
},
...
figureFilterOptions
.
poses
.
map
((
pose
)
=>
({
label
:
pose
,
value
:
pose
})),
]
}
onChange=
{
(
value
)
=>
setFigureFilters
((
current
)
=>
({
...
current
,
pose
:
value
}))
}
/>
<
Select
value=
{
figureFilters
.
background
}
popupMatchSelectWidth=
{
false
}
options=
{
[
{
label
:
'全部背景'
,
value
:
'all'
},
...
figureFilterOptions
.
backgrounds
.
map
((
background
)
=>
({
label
:
background
,
value
:
background
,
})),
]
}
onChange=
{
(
value
)
=>
setFigureFilters
((
current
)
=>
({
...
current
,
background
:
value
}))
}
/>
</
Space
>
<
div
className=
'figure-picker-scroll'
>
{
filteredFigures
.
length
>
0
?
(
<
div
className=
'figure-picker-grid'
>
{
filteredFigures
.
map
((
figure
)
=>
(
<
button
type=
'button'
key=
{
figure
.
id
}
className=
{
`picker-figure-card ${selectedFigure?.id === figure.id ? 'active' : ''}`
}
onClick=
{
()
=>
{
setSelectedFigure
(
figure
)
setPreviewFigureSelected
(
false
)
setFigurePickerOpen
(
false
)
}
}
>
<
span
className=
'picker-avatar'
>
{
figure
.
avatar
?
(
<
img
src=
{
figure
.
avatar
}
alt=
{
figure
.
name
}
/>
)
:
(
<
UserOutlined
/>
)
}
</
span
>
<
strong
>
{
figure
.
name
}
</
strong
>
{
selectedFigure
?.
id
===
figure
.
id
?
(
<
span
className=
'picker-selected-mark'
>
<
CheckOutlined
/>
</
span
>
)
:
null
}
</
button
>
))
}
</
div
>
)
:
(
<
Empty
description=
'没有符合条件的形象'
/>
)
}
</
div
>
</>
)
:
(
<
Empty
description=
'暂无可用形象'
/>
)
}
</
Modal
>
<
Modal
open=
{
voicePickerOpen
}
title=
'选择声音'
footer=
{
null
}
onCancel=
{
()
=>
setVoicePickerOpen
(
false
)
}
width=
{
620
}
className=
'digital-human-picker-modal voice-picker-modal'
>
<
div
className=
'picker-tabs'
role=
'tablist'
aria
-
label=
'声音性别'
>
<
button
type=
'button'
className=
{
voiceGender
===
'female'
?
'active'
:
''
}
onClick=
{
()
=>
setVoiceGender
(
'female'
)
}
>
女声
</
button
>
<
button
type=
'button'
className=
{
voiceGender
===
'male'
?
'active'
:
''
}
onClick=
{
()
=>
setVoiceGender
(
'male'
)
}
>
男声
</
button
>
</
div
>
<
div
className=
'voice-picker-grid'
>
{
renderVoiceGrid
(
voiceGender
===
'female'
?
femaleVoices
:
maleVoices
)
}
</
div
>
</
Modal
>
</
Modal
>
</
Modal
>
)
)
}
}
apps/admin/src/components/editor/wangeditor-customer/features/ai-media/DigitalHumanUploadField.jsx
0 → 100644
浏览文件 @
72492e6c
import
{
useState
}
from
'react'
import
{
Button
,
message
,
Upload
}
from
'antd'
import
{
CheckCircleFilled
,
DeleteOutlined
,
UploadOutlined
}
from
'@ant-design/icons'
import
{
uploadFile
}
from
'@/utils/oss'
const
IMAGE_EXTENSIONS
=
[
'png'
,
'jpg'
,
'jpeg'
,
'bmp'
]
const
VIDEO_EXTENSIONS
=
[
'mp4'
]
const
AUDIO_EXTENSIONS
=
[
'wav'
,
'mp3'
,
'wma'
,
'm4a'
]
function
getExtension
(
file
)
{
return
file
.
name
.
split
(
'.'
).
pop
()?.
toLowerCase
()
||
''
}
function
getFileCategory
(
file
)
{
const
extension
=
getExtension
(
file
)
if
(
file
.
type
.
startsWith
(
'image/'
)
||
IMAGE_EXTENSIONS
.
includes
(
extension
))
return
'image'
if
(
file
.
type
.
startsWith
(
'video/'
)
||
VIDEO_EXTENSIONS
.
includes
(
extension
))
return
'video'
if
(
file
.
type
.
startsWith
(
'audio/'
)
||
AUDIO_EXTENSIONS
.
includes
(
extension
))
return
'audio'
return
''
}
function
matchesAccept
(
file
,
accept
)
{
const
extension
=
getExtension
(
file
)
const
category
=
getFileCategory
(
file
)
return
accept
.
split
(
','
).
some
((
item
)
=>
{
const
pattern
=
item
.
trim
().
toLowerCase
()
if
(
pattern
.
startsWith
(
'.'
))
return
pattern
===
`.
${
extension
}
`
if
(
pattern
.
endsWith
(
'/*'
))
return
category
===
pattern
.
slice
(
0
,
-
2
)
return
file
.
type
.
toLowerCase
()
===
pattern
})
}
export
default
function
DigitalHumanUploadField
({
label
,
value
,
onChange
,
accept
,
maxSizeMB
,
maxImageSizeMB
,
maxVideoSizeMB
,
hint
,
uploadHint
,
previewType
,
filenamePrefix
=
'digital-human'
,
})
{
const
[
uploading
,
setUploading
]
=
useState
(
false
)
const
displayLabel
=
label
.
replace
(
'(可选)'
,
''
)
const
upload
=
async
({
file
,
onSuccess
,
onError
})
=>
{
if
(
!
matchesAccept
(
file
,
accept
))
{
const
error
=
new
Error
(
'文件格式不符合要求'
)
message
.
error
(
error
.
message
)
onError
(
error
)
return
}
const
fileLimit
=
getFileCategory
(
file
)
===
'image'
?
(
maxImageSizeMB
??
maxSizeMB
)
:
(
maxVideoSizeMB
??
maxSizeMB
)
if
(
file
.
size
>
fileLimit
*
1024
*
1024
)
{
const
error
=
new
Error
(
`文件不能超过
${
fileLimit
}
MB`
)
message
.
error
(
error
.
message
)
onError
(
error
)
return
}
setUploading
(
true
)
try
{
const
url
=
await
uploadFile
(
file
,
{
filenamePrefix
})
onChange
(
url
)
onSuccess
({
url
})
message
.
success
(
`
${
label
}
上传成功`
)
}
catch
(
error
)
{
message
.
error
(
`
${
label
}
上传失败`
)
onError
(
error
)
}
finally
{
setUploading
(
false
)
}
}
if
(
previewType
===
'image'
)
{
return
(
<
div
className=
'media-field image-media-field'
>
<
div
className=
'field-label'
>
{
label
}
</
div
>
<
div
className=
{
`image-upload-card ${value ? 'has-value' : ''}`
}
>
<
Upload
className=
'image-upload-trigger'
accept=
{
accept
}
showUploadList=
{
false
}
customRequest=
{
upload
}
>
<
button
type=
'button'
className=
'image-upload-button'
disabled=
{
uploading
}
>
{
value
?
(
<
img
src=
{
value
}
alt=
{
`${label}预览`
}
/>
)
:
(
<
span
className=
'image-upload-icon'
>
<
UploadOutlined
/>
</
span
>
)
}
<
span
className=
'image-upload-copy'
>
<
strong
>
{
uploading
?
'正在上传…'
:
value
?
`已选择${displayLabel}`
:
`上传${displayLabel}`
}
</
strong
>
<
small
>
{
value
?
'点击更换图片'
:
uploadHint
||
`文件不能超过 ${maxSizeMB}MB`
}
</
small
>
</
span
>
</
button
>
</
Upload
>
{
value
?
(
<
Button
type=
'text'
icon=
{
<
DeleteOutlined
/>
}
className=
'image-upload-remove'
onClick=
{
()
=>
onChange
(
''
)
}
aria
-
label=
{
`清除${label}`
}
/>
)
:
null
}
</
div
>
{
hint
?
<
div
className=
'field-hint'
>
{
hint
}
</
div
>
:
null
}
</
div
>
)
}
return
(
<
div
className=
'media-field'
>
<
div
className=
'field-label'
>
{
label
}
</
div
>
<
div
className=
{
`teacher-upload ${value ? 'has-value' : ''}`
}
>
<
Upload
accept=
{
accept
}
showUploadList=
{
false
}
customRequest=
{
upload
}
>
<
Button
icon=
{
<
UploadOutlined
/>
}
loading=
{
uploading
}
>
{
value
?
`重新上传${label}`
:
`上传${label}`
}
</
Button
>
</
Upload
>
{
value
?
(
<
span
className=
'upload-ready'
>
<
CheckCircleFilled
/>
已准备好
</
span
>
)
:
null
}
{
value
?
(
<
Button
type=
'text'
size=
'small'
icon=
{
<
DeleteOutlined
/>
}
onClick=
{
()
=>
onChange
(
''
)
}
aria
-
label=
{
`清除${label}`
}
>
清除
</
Button
>
)
:
null
}
</
div
>
{
value
&&
previewType
===
'video'
?
(
<
div
className=
'source-preview video-preview'
>
<
video
src=
{
value
}
controls
preload=
'metadata'
aria
-
label=
{
`${label}预览`
}
/>
<
span
>
请播放检查画面、时长和声音是否正确
</
span
>
</
div
>
)
:
null
}
{
hint
?
<
div
className=
'field-hint'
>
{
hint
}
</
div
>
:
null
}
</
div
>
)
}
apps/admin/src/components/editor/wangeditor-customer/features/ai-media/digital-human.less
浏览文件 @
72492e6c
.digital-human-modal {
--dh-accent: var(--main-color);
--dh-accent-soft: color-mix(in srgb, var(--main-color) 8%, white);
--dh-focus: color-mix(in srgb, var(--main-color) 18%, transparent);
--dh-ink: rgb(0 0 0 / 88%);
--dh-muted: rgb(0 0 0 / 45%);
--dh-line: #d9d9d9;
--dh-canvas: #f8f6f7;
max-width: calc(100vw - 24px);
.ant-modal-content {
overflow: hidden;
border-radius: 10px;
}
.ant-modal-body {
max-height: calc(100vh - 96px);
overflow-y: auto;
overscroll-behavior: contain;
}
.ant-btn-primary {
border-color: var(--dh-accent);
background: var(--dh-accent);
&:not(:disabled):hover,
&:not(:disabled):focus {
border-color: var(--dh-accent);
background: color-mix(in srgb, var(--dh-accent) 88%, black);
}
}
.ant-btn-link {
color: var(--dh-accent);
}
.ant-checkbox-checked .ant-checkbox-inner,
.ant-checkbox-wrapper:not(.ant-checkbox-wrapper-disabled):hover .ant-checkbox-inner {
border-color: var(--dh-accent);
background: var(--dh-accent);
}
.ant-input:focus,
.ant-input-focused,
.ant-input-number-focused,
.ant-select-focused .ant-select-selector {
border-color: var(--dh-accent) !important;
box-shadow: 0 0 0 2px var(--dh-focus) !important;
}
}
.digital-human-modal-title {
display: flex;
flex-direction: column;
gap: 2px;
> span {
color: #1c1820;
font-size: 16px;
font-weight: 600;
}
small {
color: #8f858d;
font-size: 12px;
font-weight: 400;
}
}
.ai-digital-human {
.ai-digital-human {
.section-box {
color: var(--dh-ink);
.digital-human-workspace-tabs > .ant-tabs-nav {
margin-bottom: 20px;
}
.component-layout {
display: grid;
grid-template-columns: minmax(320px, 360px) minmax(0, 1fr);
gap: 18px;
align-items: start;
&.is-hidden {
display: none;
}
}
.config-column {
min-width: 0;
max-height: calc(100vh - 190px);
padding-right: 6px;
overflow-y: auto;
scrollbar-width: thin;
}
.preview-panel {
position: sticky;
top: 0;
min-width: 0;
padding: 18px;
border-radius: 10px;
background: var(--dh-canvas);
}
.preview-panel-heading {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 14px;
margin-bottom: 14px;
padding: 12px;
background: #fff;
border-radius: 8px;
border: 1px solid #f0f0f0;
.section-title {
> div {
font-size: 15px;
display: flex;
min-width: 0;
flex-direction: column;
gap: 4px;
}
strong {
color: var(--dh-ink);
font-size: 17px;
font-weight: 650;
}
span {
color: var(--dh-muted);
font-size: 12px;
}
.ant-btn {
flex: 0 0 auto;
color: var(--dh-muted);
}
}
.config-section {
margin-bottom: 24px;
h2 {
margin: 0 0 12px;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600;
font-weight: 600;
color: #333;
margin-bottom: 10px;
border-left: 4px solid #ab1941;
padding-left: 10px;
}
}
}
}
.
figure-list
{
.
selected-media-card
{
display: flex;
display: flex;
overflow-x: auto;
width: 100%;
min-height: 80px;
align-items: center;
gap: 12px;
gap: 12px;
padding: 6px 0;
padding: 8px 12px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
transition:
border-color 0.18s ease,
background 0.18s ease;
&::-webkit-scrollbar {
&:hover:not(:disabled) {
height: 6px;
border-color: var(--dh-accent);
}
background: var(--dh-accent-soft);
&::-webkit-scrollbar-thumb {
background: #e8e8e8;
border-radius: 3px;
}
}
.figure-item {
&:focus-visible {
flex: 0 0 92px;
outline: 3px solid var(--dh-focus);
cursor: pointer;
outline-offset: 2px;
text-align: center;
}
padding: 8px;
border: 2px solid transparent;
border-radius: 12px;
transition: all 0.3s;
&.active {
&:disabled {
border-color: #ab1941;
cursor: wait;
background: rgba(171, 25, 65, 0.08);
opacity: 0.72;
}
}
}
.avatar-wrapper {
.selected-media-thumb {
display: grid;
width: 72px;
width: 72px;
height: 72px;
height: 56px;
margin: 0 auto 6px;
flex: 0 0 72px;
position: relative;
overflow: hidden;
border-radius: 6px;
background: #eee9eb;
color: #9a9298;
font-size: 22px;
place-items: center;
.figure-avatar
{
img
{
width: 72px
;
display: block
;
height: 72px
;
width: 100%
;
border-radius: 5
0%;
height: 10
0%;
object-fit: cover;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
background: #f0f0f0;
}
}
.avatar-fallback {
.selected-media-copy {
width: 72px;
height: 72px;
border-radius: 50%;
background: #f0f0f0;
display: flex;
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 4px;
strong {
overflow: hidden;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
> span:not(.voice-tags) {
display: inline-flex;
align-items: center;
align-items: center;
justify-content: center;
gap: 4px;
font-size: 28px;
color: var(--dh-muted);
color: #bfbfbf;
font-size: 13px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
}
}
.figure-name {
.selected-media-action {
font-size: 13px;
flex: 0 0 auto;
color: #555;
color: #776f76;
white-space: nowrap;
font-size: 18px;
overflow: hidden;
text-overflow: ellipsis;
}
}
.voice-card {
min-height: 88px;
.selected-media-thumb {
width: 64px;
height: 64px;
flex-basis: 64px;
}
}
}
}
.voice-selection {
.voice-avatar {
.sub-title {
background: #f0edef;
font-size: 13px;
color: #8d6872;
font-size: 30px;
}
.voice-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
em {
padding: 2px 6px;
border-radius: 5px;
background: #f0edef;
color: #776f76;
font-size: 11px;
font-style: normal;
}
}
.config-collapse {
margin: 0 0 24px;
> .ant-collapse-item > .ant-collapse-header {
align-items: center;
padding: 0 0 12px;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600;
}
> .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box {
padding: 0;
}
}
.field-label {
margin: 0 0 8px;
color: var(--dh-ink);
font-size: 14px;
font-weight: 500;
font-weight: 500;
}
.field-hint {
margin-top: 6px;
color: #8f858d;
font-size: 12px;
line-height: 1.5;
}
.full-control {
width: 100%;
margin-bottom: 16px;
}
.range-setting {
margin: 0 0 16px;
.range-label {
margin-bottom: 8px;
margin-bottom: 8px;
color: #666;
color: var(--dh-ink);
font-size: 14px;
font-weight: 500;
}
}
.
voice-grid
{
.
range-control
{
display: grid;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-columns: 18px minmax(60px, 1fr) 18px 56px;
gap: 6px;
align-items: center;
max-height: 176px;
gap: 8px;
overflow-y: auto;
padding-right: 4px;
}
}
.voice-item {
.range-edge {
color: var(--dh-muted);
font-size: 12px;
text-align: center;
}
.ant-slider {
margin: 4px 6px;
}
.ant-input-number {
width: 56px;
}
}
.script-section {
margin-top: 4px;
.field-label {
margin-bottom: 12px;
}
}
.script-box {
overflow: hidden;
border: 1px solid var(--dh-line);
border-radius: 6px;
background: #fff;
.ant-input {
min-height: 160px;
padding: 8px 11px;
color: var(--dh-ink);
font-size: 14px;
line-height: 1.57;
resize: vertical;
}
.ant-input-show-count-suffix {
padding-right: 11px;
color: #aa9fa6;
font-size: 12px;
}
}
.output-collapse {
margin-top: 0;
}
.output-settings {
display: flex;
display: flex;
flex-direction: column;
gap: 4px;
> .ant-input {
margin-top: 8px;
}
.teacher-upload {
margin-top: 6px;
}
> .advanced-field,
> .media-field {
margin-top: 12px;
}
.advanced-field .full-control {
margin-bottom: 0;
}
}
.choice-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin-bottom: 16px;
button {
display: grid;
min-height: 64px;
grid-template-columns: 40px 1fr;
align-content: center;
align-items: center;
align-items: center;
justify-content: start;
column-gap: 8px;
padding: 6px 10px;
padding: 6px 10px;
border: 1px solid
#e8e8e8
;
border: 1px solid
var(--dh-line)
;
border-radius: 6px;
border-radius: 6px;
background: #fff;
color: var(--dh-ink);
cursor: pointer;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
font-weight: 400;
text-align: left;
&:hover,
&.active {
&.active {
border-color: #ab1941;
border-color: var(--dh-accent);
background: rgba(171, 25, 65, 0.08);
background: var(--dh-accent-soft);
}
color: var(--dh-accent);
&:hover {
background: #fafafa;
}
}
.v-info {
&:focus-visible {
flex: 1;
outline: 3px solid var(--dh-focus);
display: flex;
outline-offset: 2px;
flex-direction: column;
}
overflow: hidden;
.v-name {
small {
font-size: 13px;
grid-column: 2;
font-weight: 500;
color: var(--dh-muted);
font-size: 12px;
font-weight: 400;
}
}
}
.v-style {
font-size: 11px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.ratio-icon {
display: block;
width: 30px;
height: 18px;
grid-row: span 2;
justify-self: center;
border: 2px solid currentColor;
border-radius: 3px;
&.portrait {
width: 18px;
height: 30px;
}
}
}
}
.template-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
margin-bottom: 12px;
}
}
.upload-status {
.template-card {
margin-top: 10px;
position: relative;
color: #52c41a;
display: flex;
font-size: 13px;
min-width: 0;
padding: 7px;
flex-direction: column;
gap: 7px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
&.active {
border-color: var(--dh-accent);
background: var(--dh-accent-soft);
}
}
}
.preview-panel {
.template-preview {
.results-container {
min-height: 320px;
background: #f7f8fa;
border-radius: 12px;
border: 2px dashed #e1e4e8;
display: flex;
display: flex;
height: 100px;
align-items: center;
align-items: center;
justify-content: center;
justify-content: center;
padding: 16px;
overflow: hidden;
border-radius: 7px;
background: #f5f1f2;
.empty-preview {
img {
text-align: center;
width: 100%;
color: #abb2bb;
height: 100%;
p {
object-fit: contain;
font-size: 14px;
margin-top: 16px;
}
}
}
}
.generating-box {
.template-meta {
text-align: center;
display: flex;
.gen-text {
justify-content: space-between;
font-weight: 600;
gap: 6px;
margin: 16px 0 8px;
color: #333;
strong {
font-size: 12px;
}
}
.gen-hint {
font-size: 13px;
small {
color: #999;
color: var(--dh-muted);
font-size: 10px;
}
}
}
}
.final-video-box {
.template-selected {
width: 100%;
position: absolute;
video {
top: 8px;
width: 100%;
right: 8px;
color: var(--dh-accent);
}
.inline-hint {
padding: 10px 12px;
border-radius: 8px;
border-radius: 8px;
background: #000;
background: var(--dh-canvas);
max-height: 300px;
color: var(--dh-muted);
font-size: 12px;
line-height: 1.5;
}
}
.video-actions {
margin-top: 14px;
.media-field {
.field-label {
margin-bottom: 6px;
font-size: 14px;
}
.teacher-upload {
display: flex;
display: flex;
flex-direction: column;
min-height: 40px;
gap: 10px;
align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 8px;
border: 1px dashed #ddd4d8;
border-radius: 10px;
background: #fff;
&.has-value {
border-style: solid;
border-color: #b7d9c4;
background: #f3faf5;
}
}
}
}
.field-hint {
margin-top: 6px;
color: #8f858d;
font-size: 12px;
line-height: 1.5;
}
}
.
submit-btn
{
.
image-upload-card
{
height: 44px
;
position: relative
;
font-size: 15px
;
overflow: hidden
;
font-weight: 600
;
border: 1px dashed var(--dh-line)
;
border-radius: 8px;
border-radius: 8px;
margin-top: 16px;
background: #fff;
transition: border-color 0.2s;
&:hover,
&:focus-within {
border-color: var(--dh-accent);
}
&.has-value {
border-style: solid;
}
}
.image-upload-trigger,
.image-upload-trigger .ant-upload {
display: block;
width: 100%;
}
}
.image-upload-button {
display: flex;
width: 100%;
min-height: 72px;
align-items: center;
gap: 12px;
padding: 8px 44px 8px 8px;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
&:disabled {
cursor: wait;
opacity: 0.65;
}
img {
display: block;
width: 88px;
height: 54px;
flex: 0 0 88px;
border-radius: 6px;
background: var(--dh-canvas);
object-fit: cover;
}
}
.image-upload-icon {
display: grid;
width: 48px;
height: 48px;
flex: 0 0 48px;
border-radius: 6px;
background: var(--dh-canvas);
color: var(--dh-muted);
font-size: 20px;
place-items: center;
}
.image-upload-copy {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 4px;
strong {
color: var(--dh-ink);
font-size: 14px;
font-weight: 500;
}
small {
color: var(--dh-muted);
font-size: 12px;
}
}
.image-upload-remove {
position: absolute;
top: 50%;
right: 8px;
color: var(--dh-muted);
transform: translateY(-50%);
&:hover {
color: #ff4d4f;
}
}
.source-preview {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 8px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: var(--dh-canvas);
img,
video {
display: block;
width: 112px;
height: 72px;
border-radius: 6px;
background: #1d181a;
object-fit: cover;
}
}
}
.baidu-upload-list {
display: flex;
margin-top: 8px;
flex-direction: column;
gap: 4px;
}
.baidu-upload-item {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 6px 4px 10px;
border-radius: 6px;
background: var(--dh-canvas);
span {
overflow: hidden;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.generation-result {
margin-top: 0;
}
.generation-result-heading {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 22px;
margin-bottom: 10px;
color: var(--dh-muted);
font-size: 13px;
strong {
font-weight: 500;
}
}
.preview-stage {
position: relative;
display: grid;
width: 100%;
min-height: 0;
overflow: visible;
background: #fff;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
place-items: center;
&.landscape {
aspect-ratio: 16 / 9;
}
&.portrait {
width: min(100%, 315px);
margin-inline: auto;
aspect-ratio: 9 / 16;
}
}
.preview-avatar {
display: grid;
width: 110px;
height: 138px;
overflow: hidden;
border-radius: 12px;
background: #e5dfe2;
color: #9a9298;
font-size: 34px;
place-items: center;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.empty-generation {
position: relative;
width: 100%;
height: 100%;
min-height: 0;
align-self: stretch;
justify-self: stretch;
}
.preview-canvas-clip {
position: absolute;
z-index: 1;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.preview-image-layer {
position: absolute;
display: grid;
color: #9a9298;
font-size: 34px;
place-items: center;
will-change: left, top, transform;
img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
}
.preview-layer {
position: absolute;
z-index: 2;
display: grid;
width: 100%;
height: 100%;
box-sizing: border-box;
border: 2px solid transparent;
border-radius: 10px;
color: #9a9298;
font-size: 34px;
cursor: pointer;
place-items: center;
touch-action: none;
user-select: none;
will-change: left, top, transform;
&.selected {
border-color: var(--main-color);
cursor: grab;
}
&.selected:active {
cursor: grabbing;
}
.preview-resize-handle {
position: absolute;
z-index: 2;
display: block;
width: 12px;
height: 12px;
border: 2px solid var(--dh-accent);
border-radius: 50%;
background: #fff;
cursor: nwse-resize;
&.nw {
top: -6px;
left: -6px;
cursor: nwse-resize;
}
&.ne {
top: -6px;
right: -6px;
cursor: nesw-resize;
}
&.sw {
bottom: -6px;
left: -6px;
cursor: nesw-resize;
}
&.se {
right: -6px;
bottom: -6px;
cursor: nwse-resize;
}
}
}
.generating-box {
position: absolute;
z-index: 5;
inset: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
gap: 12px;
background: rgb(255 255 255 / 82%);
color: var(--dh-muted);
}
.generated-preview-clip {
position: absolute;
z-index: 3;
inset: 0;
overflow: hidden;
background: #000;
}
.generated-preview-video {
display: block;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
max-width: 100%;
max-height: 100%;
background: #000;
object-fit: contain;
}
.video-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 14px;
}
.generation-submit {
height: 48px;
margin-top: 16px;
border-radius: 10px;
font-size: 16px;
font-weight: 600;
}
.task-history {
margin-top: 24px;
margin-bottom: 0;
padding: 16px;
border: 1px solid var(--dh-line);
border-radius: 14px;
}
.task-history .section-title {
margin-bottom: 12px;
font-size: 17px;
font-weight: 650;
}
.task-list {
display: grid;
gap: 8px;
}
.task-item {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: center;
gap: 10px;
padding: 9px 12px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
text-align: left;
&:hover,
&.active {
border-color: var(--dh-accent);
background: var(--dh-accent-soft);
}
}
.task-name {
overflow: hidden;
font-weight: 500;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-time {
color: #8f858d;
font-size: 12px;
}
.customize-layout {
display: grid;
grid-template-columns: minmax(320px, 420px) minmax(0, 1fr);
gap: 18px;
}
.customize-form-card,
.customize-result-card {
padding: 24px;
border: 1px solid var(--dh-line);
border-radius: 10px;
background: #fff;
h2 {
margin: 0;
font-size: 17px;
}
}
.customize-form-card {
> p {
margin: 8px 0 20px;
color: var(--dh-muted);
}
.media-field {
margin-bottom: 24px;
}
}
.customize-result-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 20px;
}
.customize-task {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
border-radius: 8px;
background: var(--dh-canvas);
img {
width: 112px;
height: 112px;
flex: 0 0 112px;
border-radius: 8px;
object-fit: cover;
}
> div {
display: flex;
min-width: 0;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
span,
small {
color: var(--dh-muted);
}
}
.customize-empty {
display: grid;
min-height: 260px;
border-radius: 8px;
background: var(--dh-canvas);
color: var(--dh-muted);
place-items: center;
}
}
@media (max-width: 900px) {
.ai-digital-human {
.component-layout,
.customize-layout {
grid-template-columns: 1fr;
}
.config-column {
max-height: none;
padding-right: 0;
overflow: visible;
}
.preview-panel {
position: static;
}
}
}
.digital-human-picker-modal {
.ant-modal-content {
border-radius: 14px;
}
.ant-modal-body {
max-height: 65vh;
overflow-y: auto;
}
}
.figure-picker-modal .ant-modal-body {
overflow: hidden;
}
.picker-loading {
display: grid;
min-height: 160px;
place-items: center;
}
.figure-picker-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 22px 18px;
}
.figure-picker-scroll {
max-height: 62vh;
overflow-y: auto;
padding: 4px 4px 8px 0;
}
.picker-figure-card {
position: relative;
display: flex;
min-width: 0;
align-items: stretch;
padding: 0;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
flex-direction: column;
font: inherit;
text-align: center;
&:hover,
&.active {
.picker-avatar {
border-color: var(--main-color);
}
}
&:focus-visible {
outline: 3px solid color-mix(in srgb, var(--main-color) 35%, transparent);
outline-offset: 4px;
}
strong {
display: block;
overflow: hidden;
margin-top: 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.picker-selected-mark {
position: absolute;
top: 10px;
right: 10px;
display: grid;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--main-color);
color: #fff;
place-items: center;
}
.picker-avatar {
display: grid;
width: 100%;
aspect-ratio: 3 / 4;
overflow: hidden;
border-radius: 10px;
border: 2px solid transparent;
background: #f0f0f5;
color: #9a9298;
place-items: center;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.picker-tabs {
display: flex;
gap: 8px;
margin-bottom: 14px;
button {
min-width: 84px;
padding: 7px 16px;
border: 1px solid #e5dfe2;
border-radius: 8px;
background: #fff;
color: #756c75;
cursor: pointer;
&.active {
border-color: var(--main-color);
background: color-mix(in srgb, var(--main-color) 8%, white);
color: var(--main-color);
}
}
}
.voice-picker-grid {
.voice-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.voice-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border: 1px solid #e5dfe2;
border-radius: 10px;
background: #fff;
cursor: pointer;
&.active {
border-color: var(--main-color);
background: color-mix(in srgb, var(--main-color) 8%, white);
}
}
.voice-picker-avatar {
display: grid;
width: 38px;
height: 38px;
flex: 0 0 38px;
border-radius: 50%;
background: #f0edef;
color: #8d6872;
place-items: center;
}
.v-info {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 2px;
strong,
small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
small {
color: #8f858d;
font-size: 11px;
}
}
}
@media (max-width: 640px) {
.digital-human-picker-modal {
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px);
.figure-picker-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px 10px;
}
}
.digital-human-modal {
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px);
.ant-modal-body {
padding: 12px;
}
}
.ai-digital-human {
.selected-media-card {
gap: 12px;
padding: 12px;
border-radius: 14px;
}
.preview-panel {
padding: 14px;
}
.preview-stage,
.generating-box {
min-height: 360px;
}
.final-video-box video {
height: 360px;
}
.selected-media-thumb {
width: 92px;
height: 72px;
flex-basis: 92px;
}
.voice-card .selected-media-thumb {
width: 72px;
height: 72px;
flex-basis: 72px;
}
.selected-media-copy {
strong {
font-size: 18px;
}
> span:not(.voice-tags) {
font-size: 13px;
}
}
.voice-tags em {
font-size: 11px;
}
.range-setting {
.range-control {
grid-template-columns: 18px minmax(60px, 1fr) 18px 64px;
gap: 6px;
}
.ant-input-number {
width: 64px;
}
}
.script-preview-bar {
align-items: stretch;
flex-direction: column;
gap: 12px;
.ant-btn {
width: 100%;
}
}
.choice-grid,
.figure-picker-grid,
.voice-picker-grid .voice-grid {
gap: 10px;
}
.template-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.task-item {
grid-template-columns: minmax(0, 1fr) auto;
.task-time {
display: none;
}
}
}
}
@media (prefers-reduced-motion: reduce) {
.selected-media-card,
.template-card,
.voice-item {
transition: none;
}
}
}
}
apps/learning/package.json
浏览文件 @
72492e6c
...
@@ -21,7 +21,7 @@
...
@@ -21,7 +21,7 @@
"nuqs"
:
"^2.9.0"
,
"nuqs"
:
"^2.9.0"
,
"react"
:
"^19.2.7"
,
"react"
:
"^19.2.7"
,
"react-dom"
:
"^19.2.7"
,
"react-dom"
:
"^19.2.7"
,
"react-router"
:
"^7.
4.0
"
,
"react-router"
:
"^7.
18.2
"
,
"zustand"
:
"^5.0.3"
"zustand"
:
"^5.0.3"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
...
@@ -31,13 +31,13 @@
...
@@ -31,13 +31,13 @@
"@types/node"
:
"^22.13.9"
,
"@types/node"
:
"^22.13.9"
,
"@types/react"
:
"^19.2.17"
,
"@types/react"
:
"^19.2.17"
,
"@types/react-dom"
:
"^19.2.3"
,
"@types/react-dom"
:
"^19.2.3"
,
"@vitejs/plugin-react"
:
"^6.0.
3
"
,
"@vitejs/plugin-react"
:
"^6.0.
5
"
,
"jsdom"
:
"^29.1.1"
,
"jsdom"
:
"^29.1.1"
,
"oxfmt"
:
"^0.59.0"
,
"oxfmt"
:
"^0.59.0"
,
"oxlint"
:
"^1.74.0"
,
"oxlint"
:
"^1.74.0"
,
"typescript"
:
"^7.0.2"
,
"typescript"
:
"^7.0.2"
,
"vite"
:
"^8.
1.5
"
,
"vite"
:
"^8.
2.1
"
,
"vite-plugin-mkcert"
:
"^
1.17.1
0"
,
"vite-plugin-mkcert"
:
"^
2.1.
0"
,
"vitest"
:
"^4.1.10"
"vitest"
:
"^4.1.10"
}
}
}
}
pnpm-lock.yaml
浏览文件 @
72492e6c
...
@@ -18,7 +18,7 @@ importers:
...
@@ -18,7 +18,7 @@ importers:
version
:
2.2.5
version
:
2.2.5
'
@ezijing/ai-react'
:
'
@ezijing/ai-react'
:
specifier
:
^1.0.40
specifier
:
^1.0.40
version
:
1.0.44(debug@4.4.3
)(react@18.3.1
)
version
:
1.0.44(debug@4.4.3
(supports-color@10.2.2))(react@18.3.1)(supports-color@10.2.2
)
'
@fortaine/fetch-event-source'
:
'
@fortaine/fetch-event-source'
:
specifier
:
^3.0.6
specifier
:
^3.0.6
version
:
3.0.6
version
:
3.0.6
...
@@ -30,19 +30,19 @@ importers:
...
@@ -30,19 +30,19 @@ importers:
version
:
5.1.23
version
:
5.1.23
'
@wangeditor/editor-for-react'
:
'
@wangeditor/editor-for-react'
:
specifier
:
^1.0.6
specifier
:
^1.0.6
version
:
1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ahooks
:
ahooks
:
specifier
:
^3.8.0
specifier
:
^3.8.0
version
:
3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ali-oss
:
ali-oss
:
specifier
:
^6.20.0
specifier
:
^6.20.0
version
:
6.23.0
version
:
6.23.0
(supports-color@10.2.2)
antd
:
antd
:
specifier
:
^5.21.6
specifier
:
^5.21.6
version
:
5.29.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
5.29.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
axios
:
axios
:
specifier
:
^1.18.1
specifier
:
^1.18.1
version
:
1.18.1(debug@4.4.3)
version
:
1.18.1(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)
dayjs
:
dayjs
:
specifier
:
^1.11.11
specifier
:
^1.11.11
version
:
1.11.21
version
:
1.11.21
...
@@ -50,8 +50,8 @@ importers:
...
@@ -50,8 +50,8 @@ importers:
specifier
:
^0.0.2-alpha.1
specifier
:
^0.0.2-alpha.1
version
:
0.0.2-alpha.1
version
:
0.0.2-alpha.1
echarts
:
echarts
:
specifier
:
^
5.4.3
specifier
:
^
6.1.0
version
:
5.6
.0
version
:
6.1
.0
file-saver
:
file-saver
:
specifier
:
^2.0.5
specifier
:
^2.0.5
version
:
2.0.5
version
:
2.0.5
...
@@ -62,23 +62,23 @@ importers:
...
@@ -62,23 +62,23 @@ importers:
specifier
:
^3.7.1
specifier
:
^3.7.1
version
:
3.7.1
version
:
3.7.1
js-cookie
:
js-cookie
:
specifier
:
^3.0.
5
specifier
:
^3.0.
8
version
:
3.0.8
version
:
3.0.8
js-md5
:
js-md5
:
specifier
:
^0.8.3
specifier
:
^0.8.3
version
:
0.8.3
version
:
0.8.3
lodash-es
:
lodash-es
:
specifier
:
^4.1
7.2
1
specifier
:
^4.1
8.
1
version
:
4.18.1
version
:
4.18.1
markdown-it
:
markdown-it
:
specifier
:
^14.
1
.0
specifier
:
^14.
3
.0
version
:
14.3.0
version
:
14.3.0
mathjax
:
mathjax
:
specifier
:
3.2.2
specifier
:
3.2.2
version
:
3.2.2
version
:
3.2.2
nanoid
:
nanoid
:
specifier
:
^3.3.8
specifier
:
^3.3.
1
8
version
:
3.3.1
6
version
:
3.3.1
8
react
:
react
:
specifier
:
^18.3.1
specifier
:
^18.3.1
version
:
18.3.1
version
:
18.3.1
...
@@ -89,8 +89,8 @@ importers:
...
@@ -89,8 +89,8 @@ importers:
specifier
:
^4.0.13
specifier
:
^4.0.13
version
:
4.1.2(react@18.3.1)
version
:
4.1.2(react@18.3.1)
react-router
:
react-router
:
specifier
:
^7.18.
1
specifier
:
^7.18.
2
version
:
7.18.
1
(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
7.18.
2
(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-use
:
react-use
:
specifier
:
^17.6.0
specifier
:
^17.6.0
version
:
17.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
17.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...
@@ -117,8 +117,8 @@ importers:
...
@@ -117,8 +117,8 @@ importers:
specifier
:
^18.3.0
specifier
:
^18.3.0
version
:
18.3.7(@types/react@18.3.31)
version
:
18.3.7(@types/react@18.3.31)
'
@vitejs/plugin-react'
:
'
@vitejs/plugin-react'
:
specifier
:
^6.0.
3
specifier
:
^6.0.
5
version
:
6.0.
3(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
6.0.
5(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
jsdom
:
jsdom
:
specifier
:
^29.1.1
specifier
:
^29.1.1
version
:
29.1.1
version
:
29.1.1
...
@@ -135,14 +135,14 @@ importers:
...
@@ -135,14 +135,14 @@ importers:
specifier
:
^7.0.2
specifier
:
^7.0.2
version
:
7.0.2
version
:
7.0.2
vite
:
vite
:
specifier
:
^8.
1.5
specifier
:
^8.
2.1
version
:
8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
version
:
8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite-plugin-mkcert
:
vite-plugin-mkcert
:
specifier
:
^
1.17.1
0
specifier
:
^
2.1.
0
version
:
1.17.12(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
2.1.0(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
vitest
:
vitest
:
specifier
:
^4.1.10
specifier
:
^4.1.10
version
:
4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
apps/learning
:
apps/learning
:
dependencies
:
dependencies
:
...
@@ -154,19 +154,19 @@ importers:
...
@@ -154,19 +154,19 @@ importers:
version
:
5.101.2(react@19.2.7)
version
:
5.101.2(react@19.2.7)
ali-oss
:
ali-oss
:
specifier
:
^6.20.0
specifier
:
^6.20.0
version
:
6.23.0
version
:
6.23.0
(supports-color@10.2.2)
antd
:
antd
:
specifier
:
^6.5.1
specifier
:
^6.5.1
version
:
6.5.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
version
:
6.5.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
axios
:
axios
:
specifier
:
^1.8.4
specifier
:
^1.8.4
version
:
1.18.1(debug@4.4.3)
version
:
1.18.1(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)
js-md5
:
js-md5
:
specifier
:
^0.8.3
specifier
:
^0.8.3
version
:
0.8.3
version
:
0.8.3
nuqs
:
nuqs
:
specifier
:
^2.9.0
specifier
:
^2.9.0
version
:
2.9.0(react-router-dom@7.18.
1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
version
:
2.9.0(react-router-dom@7.18.
2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
react
:
react
:
specifier
:
^19.2.7
specifier
:
^19.2.7
version
:
19.2.7
version
:
19.2.7
...
@@ -174,8 +174,8 @@ importers:
...
@@ -174,8 +174,8 @@ importers:
specifier
:
^19.2.7
specifier
:
^19.2.7
version
:
19.2.7(react@19.2.7)
version
:
19.2.7(react@19.2.7)
react-router
:
react-router
:
specifier
:
^7.
4.0
specifier
:
^7.
18.2
version
:
7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
version
:
7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
zustand
:
zustand
:
specifier
:
^5.0.3
specifier
:
^5.0.3
version
:
5.0.14(@types/react@19.2.17)(immer@9.0.21)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7))
version
:
5.0.14(@types/react@19.2.17)(immer@9.0.21)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7))
...
@@ -199,8 +199,8 @@ importers:
...
@@ -199,8 +199,8 @@ importers:
specifier
:
^19.2.3
specifier
:
^19.2.3
version
:
19.2.3(@types/react@19.2.17)
version
:
19.2.3(@types/react@19.2.17)
'
@vitejs/plugin-react'
:
'
@vitejs/plugin-react'
:
specifier
:
^6.0.
3
specifier
:
^6.0.
5
version
:
6.0.
3(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
6.0.
5(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
jsdom
:
jsdom
:
specifier
:
^29.1.1
specifier
:
^29.1.1
version
:
29.1.1
version
:
29.1.1
...
@@ -214,14 +214,14 @@ importers:
...
@@ -214,14 +214,14 @@ importers:
specifier
:
^7.0.2
specifier
:
^7.0.2
version
:
7.0.2
version
:
7.0.2
vite
:
vite
:
specifier
:
^8.
1.5
specifier
:
^8.
2.1
version
:
8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
version
:
8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite-plugin-mkcert
:
vite-plugin-mkcert
:
specifier
:
^
1.17.1
0
specifier
:
^
2.1.
0
version
:
1.17.12(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
2.1.0(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
vitest
:
vitest
:
specifier
:
^4.1.10
specifier
:
^4.1.10
version
:
4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
version
:
4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
packages
:
packages
:
...
@@ -367,15 +367,6 @@ packages:
...
@@ -367,15 +367,6 @@ packages:
resolution
:
{
integrity
:
sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==
}
resolution
:
{
integrity
:
sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==
}
engines
:
{
node
:
'
>=20.19.0'
}
engines
:
{
node
:
'
>=20.19.0'
}
'
@emnapi/core@1.11.1'
:
resolution
:
{
integrity
:
sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==
}
'
@emnapi/runtime@1.11.1'
:
resolution
:
{
integrity
:
sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==
}
'
@emnapi/wasi-threads@1.2.2'
:
resolution
:
{
integrity
:
sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==
}
'
@emotion/hash@0.8.0'
:
'
@emotion/hash@0.8.0'
:
resolution
:
{
integrity
:
sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
}
resolution
:
{
integrity
:
sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
}
...
@@ -575,14 +566,8 @@ packages:
...
@@ -575,14 +566,8 @@ packages:
'
@jridgewell/trace-mapping@0.3.31'
:
'
@jridgewell/trace-mapping@0.3.31'
:
resolution
:
{
integrity
:
sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
}
resolution
:
{
integrity
:
sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
}
'
@napi-rs/wasm-runtime@1.1.6'
:
'
@oxc-project/types@0.144.0'
:
resolution
:
{
integrity
:
sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==
}
resolution
:
{
integrity
:
sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==
}
peerDependencies
:
'
@emnapi/core'
:
^1.7.1
'
@emnapi/runtime'
:
^1.7.1
'
@oxc-project/types@0.139.0'
:
resolution
:
{
integrity
:
sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==
}
'
@oxfmt/binding-android-arm-eabi@0.59.0'
:
'
@oxfmt/binding-android-arm-eabi@0.59.0'
:
resolution
:
{
integrity
:
sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==
}
resolution
:
{
integrity
:
sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==
}
...
@@ -1161,97 +1146,92 @@ packages:
...
@@ -1161,97 +1146,92 @@ packages:
peerDependencies
:
peerDependencies
:
react
:
'
>=18.2.0'
react
:
'
>=18.2.0'
'
@rolldown/binding-android-arm64@1.
1.5
'
:
'
@rolldown/binding-android-arm64@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ
==
}
resolution
:
{
integrity
:
sha512-
jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
android
]
os
:
[
android
]
'
@rolldown/binding-darwin-arm64@1.
1.5
'
:
'
@rolldown/binding-darwin-arm64@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw
==
}
resolution
:
{
integrity
:
sha512-
Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
darwin
]
os
:
[
darwin
]
'
@rolldown/binding-darwin-x64@1.
1.5
'
:
'
@rolldown/binding-darwin-x64@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6
g==
}
resolution
:
{
integrity
:
sha512-
fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqO
g==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
darwin
]
os
:
[
darwin
]
'
@rolldown/binding-freebsd-x64@1.
1.5
'
:
'
@rolldown/binding-freebsd-x64@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6T
A==
}
resolution
:
{
integrity
:
sha512-
rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9
A==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
freebsd
]
os
:
[
freebsd
]
'
@rolldown/binding-linux-arm-gnueabihf@1.
1.5
'
:
'
@rolldown/binding-linux-arm-gnueabihf@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw
==
}
resolution
:
{
integrity
:
sha512-
/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm
]
cpu
:
[
arm
]
os
:
[
linux
]
os
:
[
linux
]
'
@rolldown/binding-linux-arm64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-arm64-gnu@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q
==
}
resolution
:
{
integrity
:
sha512-
tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
'
@rolldown/binding-linux-arm64-musl@1.
1.5
'
:
'
@rolldown/binding-linux-arm64-musl@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA
==
}
resolution
:
{
integrity
:
sha512-
Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
musl
]
libc
:
[
musl
]
'
@rolldown/binding-linux-ppc64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-ppc64-gnu@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg
==
}
resolution
:
{
integrity
:
sha512-
GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
ppc64
]
cpu
:
[
ppc64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
'
@rolldown/binding-linux-s390x-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-s390x-gnu@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA
==
}
resolution
:
{
integrity
:
sha512-
p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
s390x
]
cpu
:
[
s390x
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
'
@rolldown/binding-linux-x64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-x64-gnu@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1Q
Q==
}
resolution
:
{
integrity
:
sha512-
4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhOb
Q==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
'
@rolldown/binding-linux-x64-musl@1.
1.5
'
:
'
@rolldown/binding-linux-x64-musl@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg
==
}
resolution
:
{
integrity
:
sha512-
l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
musl
]
libc
:
[
musl
]
'
@rolldown/binding-openharmony-arm64@1.
1.5
'
:
'
@rolldown/binding-openharmony-arm64@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw
==
}
resolution
:
{
integrity
:
sha512-
e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
openharmony
]
os
:
[
openharmony
]
'
@rolldown/binding-wasm32-wasi@1.1.5'
:
'
@rolldown/binding-win32-arm64-msvc@1.2.4'
:
resolution
:
{
integrity
:
sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==
}
resolution
:
{
integrity
:
sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
wasm32
]
'
@rolldown/binding-win32-arm64-msvc@1.1.5'
:
resolution
:
{
integrity
:
sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
win32
]
os
:
[
win32
]
'
@rolldown/binding-win32-x64-msvc@1.
1.5
'
:
'
@rolldown/binding-win32-x64-msvc@1.
2.4
'
:
resolution
:
{
integrity
:
sha512-
tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA
==
}
resolution
:
{
integrity
:
sha512-
UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
win32
]
os
:
[
win32
]
...
@@ -1305,9 +1285,6 @@ packages:
...
@@ -1305,9 +1285,6 @@ packages:
'
@transloadit/prettier-bytes@0.0.7'
:
'
@transloadit/prettier-bytes@0.0.7'
:
resolution
:
{
integrity
:
sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==
}
resolution
:
{
integrity
:
sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==
}
'
@tybys/wasm-util@0.10.3'
:
resolution
:
{
integrity
:
sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==
}
'
@types/aria-query@5.0.4'
:
'
@types/aria-query@5.0.4'
:
resolution
:
{
integrity
:
sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
}
resolution
:
{
integrity
:
sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
}
...
@@ -1485,8 +1462,8 @@ packages:
...
@@ -1485,8 +1462,8 @@ packages:
peerDependencies
:
peerDependencies
:
'
@uppy/core'
:
^2.3.3
'
@uppy/core'
:
^2.3.3
'
@vitejs/plugin-react@6.0.
3
'
:
'
@vitejs/plugin-react@6.0.
5
'
:
resolution
:
{
integrity
:
sha512-
vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg
==
}
resolution
:
{
integrity
:
sha512-
BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
peerDependencies
:
peerDependencies
:
'
@rolldown/plugin-babel'
:
^0.1.7 || ^0.2.0
'
@rolldown/plugin-babel'
:
^0.1.7 || ^0.2.0
...
@@ -1840,8 +1817,8 @@ packages:
...
@@ -1840,8 +1817,8 @@ packages:
easy-formula-editor@0.0.2-alpha.1
:
easy-formula-editor@0.0.2-alpha.1
:
resolution
:
{
integrity
:
sha512-SoSFappo+aYoKxztK4TlBTOIx4vFVZ487H4hXDM9cRRHbnuUJdLEiaH7yOKAwoWTeXw59KYKRVgvOFNrzU/+2A==
}
resolution
:
{
integrity
:
sha512-SoSFappo+aYoKxztK4TlBTOIx4vFVZ487H4hXDM9cRRHbnuUJdLEiaH7yOKAwoWTeXw59KYKRVgvOFNrzU/+2A==
}
echarts@
5.6
.0
:
echarts@
6.1
.0
:
resolution
:
{
integrity
:
sha512-
oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3k
A==
}
resolution
:
{
integrity
:
sha512-
q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZ
A==
}
ee-first@1.1.1
:
ee-first@1.1.1
:
resolution
:
{
integrity
:
sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
}
resolution
:
{
integrity
:
sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
}
...
@@ -2127,78 +2104,78 @@ packages:
...
@@ -2127,78 +2104,78 @@ packages:
engines
:
{
node
:
'
>=18'
}
engines
:
{
node
:
'
>=18'
}
hasBin
:
true
hasBin
:
true
lightningcss-android-arm64@1.3
2
.0
:
lightningcss-android-arm64@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlA
g==
}
resolution
:
{
integrity
:
sha512-
gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5p
g==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
android
]
os
:
[
android
]
lightningcss-darwin-arm64@1.3
2
.0
:
lightningcss-darwin-arm64@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ
==
}
resolution
:
{
integrity
:
sha512-
Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
darwin
]
os
:
[
darwin
]
lightningcss-darwin-x64@1.3
2
.0
:
lightningcss-darwin-x64@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w
==
}
resolution
:
{
integrity
:
sha512-
Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
darwin
]
os
:
[
darwin
]
lightningcss-freebsd-x64@1.3
2
.0
:
lightningcss-freebsd-x64@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHi
g==
}
resolution
:
{
integrity
:
sha512-
QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRR
g==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
freebsd
]
os
:
[
freebsd
]
lightningcss-linux-arm-gnueabihf@1.3
2
.0
:
lightningcss-linux-arm-gnueabihf@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw
==
}
resolution
:
{
integrity
:
sha512-
N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm
]
cpu
:
[
arm
]
os
:
[
linux
]
os
:
[
linux
]
lightningcss-linux-arm64-gnu@1.3
2
.0
:
lightningcss-linux-arm64-gnu@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ
==
}
resolution
:
{
integrity
:
sha512-
j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
lightningcss-linux-arm64-musl@1.3
2
.0
:
lightningcss-linux-arm64-musl@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg
==
}
resolution
:
{
integrity
:
sha512-
yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
musl
]
libc
:
[
musl
]
lightningcss-linux-x64-gnu@1.3
2
.0
:
lightningcss-linux-x64-gnu@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA
==
}
resolution
:
{
integrity
:
sha512-
ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
glibc
]
libc
:
[
glibc
]
lightningcss-linux-x64-musl@1.3
2
.0
:
lightningcss-linux-x64-musl@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg
==
}
resolution
:
{
integrity
:
sha512-
RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
linux
]
os
:
[
linux
]
libc
:
[
musl
]
libc
:
[
musl
]
lightningcss-win32-arm64-msvc@1.3
2
.0
:
lightningcss-win32-arm64-msvc@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw
==
}
resolution
:
{
integrity
:
sha512-
1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
arm64
]
cpu
:
[
arm64
]
os
:
[
win32
]
os
:
[
win32
]
lightningcss-win32-x64-msvc@1.3
2
.0
:
lightningcss-win32-x64-msvc@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q
==
}
resolution
:
{
integrity
:
sha512-
OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
cpu
:
[
x64
]
cpu
:
[
x64
]
os
:
[
win32
]
os
:
[
win32
]
lightningcss@1.3
2
.0
:
lightningcss@1.3
3
.0
:
resolution
:
{
integrity
:
sha512-
NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ
==
}
resolution
:
{
integrity
:
sha512-
WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA
==
}
engines
:
{
node
:
'
>=
12.0.0'
}
engines
:
{
node
:
'
>=
12.0.0'
}
linkify-it@5.0.2
:
linkify-it@5.0.2
:
...
@@ -2321,8 +2298,8 @@ packages:
...
@@ -2321,8 +2298,8 @@ packages:
react
:
'
*'
react
:
'
*'
react-dom
:
'
*'
react-dom
:
'
*'
nanoid@3.3.1
6
:
nanoid@3.3.1
8
:
resolution
:
{
integrity
:
sha512-
bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q
==
}
resolution
:
{
integrity
:
sha512-
DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w
==
}
engines
:
{
node
:
^10 || ^12 || ^13.7 || ^14 || >=15.0.1
}
engines
:
{
node
:
^10 || ^12 || ^13.7 || ^14 || >=15.0.1
}
hasBin
:
true
hasBin
:
true
...
@@ -2433,8 +2410,8 @@ packages:
...
@@ -2433,8 +2410,8 @@ packages:
platform@1.3.6
:
platform@1.3.6
:
resolution
:
{
integrity
:
sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
}
resolution
:
{
integrity
:
sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
}
postcss@8.5.
19
:
postcss@8.5.
26
:
resolution
:
{
integrity
:
sha512-
Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNd
Q==
}
resolution
:
{
integrity
:
sha512-
u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbk
Q==
}
engines
:
{
node
:
^10 || ^12 || >=14
}
engines
:
{
node
:
^10 || ^12 || >=14
}
preact@10.29.7
:
preact@10.29.7
:
...
@@ -2733,15 +2710,15 @@ packages:
...
@@ -2733,15 +2710,15 @@ packages:
react-is@19.2.7
:
react-is@19.2.7
:
resolution
:
{
integrity
:
sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==
}
resolution
:
{
integrity
:
sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==
}
react-router-dom@7.18.
1
:
react-router-dom@7.18.
2
:
resolution
:
{
integrity
:
sha512-
KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg
==
}
resolution
:
{
integrity
:
sha512-
AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw
==
}
engines
:
{
node
:
'
>=20.0.0'
}
engines
:
{
node
:
'
>=20.0.0'
}
peerDependencies
:
peerDependencies
:
react
:
'
>=18'
react
:
'
>=18'
react-dom
:
'
>=18'
react-dom
:
'
>=18'
react-router@7.18.
1
:
react-router@7.18.
2
:
resolution
:
{
integrity
:
sha512-
GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZA
g==
}
resolution
:
{
integrity
:
sha512-
aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCI
g==
}
engines
:
{
node
:
'
>=20.0.0'
}
engines
:
{
node
:
'
>=20.0.0'
}
peerDependencies
:
peerDependencies
:
react
:
'
>=18'
react
:
'
>=18'
...
@@ -2784,8 +2761,8 @@ packages:
...
@@ -2784,8 +2761,8 @@ packages:
resize-observer-polyfill@1.5.1
:
resize-observer-polyfill@1.5.1
:
resolution
:
{
integrity
:
sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
}
resolution
:
{
integrity
:
sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
}
rolldown@1.
1.5
:
rolldown@1.
2.4
:
resolution
:
{
integrity
:
sha512-
t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA
==
}
resolution
:
{
integrity
:
sha512-
rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w
==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
hasBin
:
true
hasBin
:
true
...
@@ -2927,6 +2904,10 @@ packages:
...
@@ -2927,6 +2904,10 @@ packages:
stylis@4.4.0
:
stylis@4.4.0
:
resolution
:
{
integrity
:
sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==
}
resolution
:
{
integrity
:
sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==
}
supports-color@10.2.2
:
resolution
:
{
integrity
:
sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==
}
engines
:
{
node
:
'
>=18'
}
symbol-tree@3.2.4
:
symbol-tree@3.2.4
:
resolution
:
{
integrity
:
sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
}
resolution
:
{
integrity
:
sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
}
...
@@ -3023,6 +3004,10 @@ packages:
...
@@ -3023,6 +3004,10 @@ packages:
resolution
:
{
integrity
:
sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==
}
resolution
:
{
integrity
:
sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==
}
engines
:
{
node
:
'
>=20.18.1'
}
engines
:
{
node
:
'
>=20.18.1'
}
undici@8.10.0
:
resolution
:
{
integrity
:
sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==
}
engines
:
{
node
:
'
>=22.19.0'
}
unescape@1.0.1
:
unescape@1.0.1
:
resolution
:
{
integrity
:
sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==
}
resolution
:
{
integrity
:
sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==
}
engines
:
{
node
:
'
>=0.10.0'
}
engines
:
{
node
:
'
>=0.10.0'
}
...
@@ -3048,19 +3033,19 @@ packages:
...
@@ -3048,19 +3033,19 @@ packages:
resolution
:
{
integrity
:
sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==
}
resolution
:
{
integrity
:
sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==
}
engines
:
{
node
:
'
>=
0.12.0'
}
engines
:
{
node
:
'
>=
0.12.0'
}
vite-plugin-mkcert@
1.17.12
:
vite-plugin-mkcert@
2.1.0
:
resolution
:
{
integrity
:
sha512-
HhLyUZnvkKLuk9o+HBaMlGIa6CqPTfLbVlLFBjrkN4mpZ4saYFRfFx5R12a46UxmzWT9dO8fijmblVvCYvCD3A
==
}
resolution
:
{
integrity
:
sha512-
UQS5iyknruwCsvqDPsKRyHIVSVfv/iGdRPmUPaM1JRwNJo8wo/MubzJ66ckWwOLxFU4wW90oZq9GviV87nRHsg
==
}
engines
:
{
node
:
'
>=
v18.0
.0'
}
engines
:
{
node
:
'
>=
22.19
.0'
}
peerDependencies
:
peerDependencies
:
vite
:
'
>=3'
vite
:
'
>=3'
vite@8.
1.5
:
vite@8.
2.1
:
resolution
:
{
integrity
:
sha512-
7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUs
w==
}
resolution
:
{
integrity
:
sha512-
EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/l
w==
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
engines
:
{
node
:
^20.19.0 || >=22.12.0
}
hasBin
:
true
hasBin
:
true
peerDependencies
:
peerDependencies
:
'
@types/node'
:
^20.19.0 || >=22.12.0
'
@types/node'
:
^20.19.0 || >=22.12.0
'
@vitejs/devtools'
:
^0.
3
.0
'
@vitejs/devtools'
:
^0.
4
.0
esbuild
:
^0.27.0 || ^0.28.0
esbuild
:
^0.27.0 || ^0.28.0
jiti
:
'
>=1.21.0'
jiti
:
'
>=1.21.0'
less
:
^4.0.0
less
:
^4.0.0
...
@@ -3188,8 +3173,8 @@ packages:
...
@@ -3188,8 +3173,8 @@ packages:
resolution
:
{
integrity
:
sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
}
resolution
:
{
integrity
:
sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
}
engines
:
{
node
:
'
>=0.4'
}
engines
:
{
node
:
'
>=0.4'
}
zrender@
5.6.1
:
zrender@
6.1.0
:
resolution
:
{
integrity
:
sha512-
OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag
==
}
resolution
:
{
integrity
:
sha512-
oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ
==
}
zustand@4.5.7
:
zustand@4.5.7
:
resolution
:
{
integrity
:
sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==
}
resolution
:
{
integrity
:
sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==
}
...
@@ -3383,22 +3368,6 @@ snapshots:
...
@@ -3383,22 +3368,6 @@ snapshots:
'
@csstools/css-tokenizer@4.0.0'
:
{}
'
@csstools/css-tokenizer@4.0.0'
:
{}
'
@emnapi/core@1.11.1'
:
dependencies
:
'
@emnapi/wasi-threads'
:
1.2.2
tslib
:
2.8.1
optional
:
true
'
@emnapi/runtime@1.11.1'
:
dependencies
:
tslib
:
2.8.1
optional
:
true
'
@emnapi/wasi-threads@1.2.2'
:
dependencies
:
tslib
:
2.8.1
optional
:
true
'
@emotion/hash@0.8.0'
:
{}
'
@emotion/hash@0.8.0'
:
{}
'
@emotion/unitless@0.7.5'
:
{}
'
@emotion/unitless@0.7.5'
:
{}
...
@@ -3483,17 +3452,17 @@ snapshots:
...
@@ -3483,17 +3452,17 @@ snapshots:
'
@exodus/bytes@1.15.1'
:
{}
'
@exodus/bytes@1.15.1'
:
{}
'
@ezijing/ai-core@1.0.44(debug@4.4.3)'
:
'
@ezijing/ai-core@1.0.44(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)'
:
dependencies
:
dependencies
:
axios
:
1.18.1(debug@4.4.3)
axios
:
1.18.1(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)
eventsource-parser
:
3.1.0
eventsource-parser
:
3.1.0
transitivePeerDependencies
:
transitivePeerDependencies
:
-
debug
-
debug
-
supports-color
-
supports-color
'
@ezijing/ai-react@1.0.44(debug@4.4.3
)(react@18.3.1
)'
:
'
@ezijing/ai-react@1.0.44(debug@4.4.3
(supports-color@10.2.2))(react@18.3.1)(supports-color@10.2.2
)'
:
dependencies
:
dependencies
:
'
@ezijing/ai-core'
:
1.0.44(debug@4.4.3)
'
@ezijing/ai-core'
:
1.0.44(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)
react
:
18.3.1
react
:
18.3.1
transitivePeerDependencies
:
transitivePeerDependencies
:
-
debug
-
debug
...
@@ -3524,14 +3493,7 @@ snapshots:
...
@@ -3524,14 +3493,7 @@ snapshots:
'
@jridgewell/sourcemap-codec'
:
1.5.5
'
@jridgewell/sourcemap-codec'
:
1.5.5
optional
:
true
optional
:
true
'
@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)'
:
'
@oxc-project/types@0.144.0'
:
{}
dependencies
:
'
@emnapi/core'
:
1.11.1
'
@emnapi/runtime'
:
1.11.1
'
@tybys/wasm-util'
:
0.10.3
optional
:
true
'
@oxc-project/types@0.139.0'
:
{}
'
@oxfmt/binding-android-arm-eabi@0.59.0'
:
'
@oxfmt/binding-android-arm-eabi@0.59.0'
:
optional
:
true
optional
:
true
...
@@ -4051,53 +4013,46 @@ snapshots:
...
@@ -4051,53 +4013,46 @@ snapshots:
dependencies
:
dependencies
:
react
:
18.3.1
react
:
18.3.1
'
@rolldown/binding-android-arm64@1.
1.5
'
:
'
@rolldown/binding-android-arm64@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-darwin-arm64@1.
1.5
'
:
'
@rolldown/binding-darwin-arm64@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-darwin-x64@1.
1.5
'
:
'
@rolldown/binding-darwin-x64@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-freebsd-x64@1.
1.5
'
:
'
@rolldown/binding-freebsd-x64@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-arm-gnueabihf@1.
1.5
'
:
'
@rolldown/binding-linux-arm-gnueabihf@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-arm64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-arm64-gnu@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-arm64-musl@1.
1.5
'
:
'
@rolldown/binding-linux-arm64-musl@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-ppc64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-ppc64-gnu@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-s390x-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-s390x-gnu@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-x64-gnu@1.
1.5
'
:
'
@rolldown/binding-linux-x64-gnu@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-linux-x64-musl@1.
1.5
'
:
'
@rolldown/binding-linux-x64-musl@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-openharmony-arm64@1.
1.5
'
:
'
@rolldown/binding-openharmony-arm64@1.
2.4
'
:
optional
:
true
optional
:
true
'
@rolldown/binding-wasm32-wasi@1.1.5'
:
'
@rolldown/binding-win32-arm64-msvc@1.2.4'
:
dependencies
:
'
@emnapi/core'
:
1.11.1
'
@emnapi/runtime'
:
1.11.1
'
@napi-rs/wasm-runtime'
:
1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional
:
true
optional
:
true
'
@rolldown/binding-win32-arm64-msvc@1.1.5'
:
'
@rolldown/binding-win32-x64-msvc@1.2.4'
:
optional
:
true
'
@rolldown/binding-win32-x64-msvc@1.1.5'
:
optional
:
true
optional
:
true
'
@rolldown/pluginutils@1.0.1'
:
{}
'
@rolldown/pluginutils@1.0.1'
:
{}
...
@@ -4159,11 +4114,6 @@ snapshots:
...
@@ -4159,11 +4114,6 @@ snapshots:
'
@transloadit/prettier-bytes@0.0.7'
:
{}
'
@transloadit/prettier-bytes@0.0.7'
:
{}
'
@tybys/wasm-util@0.10.3'
:
dependencies
:
tslib
:
2.8.1
optional
:
true
'
@types/aria-query@5.0.4'
:
{}
'
@types/aria-query@5.0.4'
:
{}
'
@types/chai@5.2.3'
:
'
@types/chai@5.2.3'
:
...
@@ -4275,7 +4225,7 @@ snapshots:
...
@@ -4275,7 +4225,7 @@ snapshots:
lodash.throttle
:
4.1.1
lodash.throttle
:
4.1.1
mime-match
:
1.0.2
mime-match
:
1.0.2
namespace-emitter
:
2.0.1
namespace-emitter
:
2.0.1
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
preact
:
10.29.7
preact
:
10.29.7
transitivePeerDependencies
:
transitivePeerDependencies
:
-
preact-render-to-string
-
preact-render-to-string
...
@@ -4291,12 +4241,12 @@ snapshots:
...
@@ -4291,12 +4241,12 @@ snapshots:
'
@uppy/companion-client'
:
2.2.2
'
@uppy/companion-client'
:
2.2.2
'
@uppy/core'
:
2.3.4
'
@uppy/core'
:
2.3.4
'
@uppy/utils'
:
4.1.3
'
@uppy/utils'
:
4.1.3
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
'
@vitejs/plugin-react@6.0.
3(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))'
:
'
@vitejs/plugin-react@6.0.
5(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))'
:
dependencies
:
dependencies
:
'
@rolldown/pluginutils'
:
1.0.1
'
@rolldown/pluginutils'
:
1.0.1
vite
:
8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite
:
8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
'
@vitest/expect@4.1.10'
:
'
@vitest/expect@4.1.10'
:
dependencies
:
dependencies
:
...
@@ -4307,13 +4257,13 @@ snapshots:
...
@@ -4307,13 +4257,13 @@ snapshots:
chai
:
6.2.2
chai
:
6.2.2
tinyrainbow
:
3.1.0
tinyrainbow
:
3.1.0
'
@vitest/mocker@4.1.10(vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))'
:
'
@vitest/mocker@4.1.10(vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))'
:
dependencies
:
dependencies
:
'
@vitest/spy'
:
4.1.10
'
@vitest/spy'
:
4.1.10
estree-walker
:
3.0.3
estree-walker
:
3.0.3
magic-string
:
0.30.21
magic-string
:
0.30.21
optionalDependencies
:
optionalDependencies
:
vite
:
8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite
:
8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
'
@vitest/pretty-format@4.1.10'
:
'
@vitest/pretty-format@4.1.10'
:
dependencies
:
dependencies
:
...
@@ -4339,25 +4289,25 @@ snapshots:
...
@@ -4339,25 +4289,25 @@ snapshots:
convert-source-map
:
2.0.0
convert-source-map
:
2.0.0
tinyrainbow
:
3.1.0
tinyrainbow
:
3.1.0
'
@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
is-url
:
1.2.4
is-url
:
1.2.4
lodash.throttle
:
4.1.1
lodash.throttle
:
4.1.1
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
prismjs
:
1.30.0
prismjs
:
1.30.0
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@types/event-emitter'
:
0.3.5
'
@types/event-emitter'
:
0.3.5
'
@uppy/core'
:
2.3.4
'
@uppy/core'
:
2.3.4
...
@@ -4374,15 +4324,15 @@ snapshots:
...
@@ -4374,15 +4324,15 @@ snapshots:
lodash.isequal
:
4.5.0
lodash.isequal
:
4.5.0
lodash.throttle
:
4.1.1
lodash.throttle
:
4.1.1
lodash.toarray
:
4.4.0
lodash.toarray
:
4.4.0
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
scroll-into-view-if-needed
:
2.2.31
scroll-into-view-if-needed
:
2.2.31
slate
:
0.72.8
slate
:
0.72.8
slate-history
:
0.66.0(slate@0.72.8)
slate-history
:
0.66.0(slate@0.72.8)
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/editor-for-react@1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
:
'
@wangeditor/editor-for-react@1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
:
dependencies
:
dependencies
:
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/editor'
:
5.1.23
'
@wangeditor/editor'
:
5.1.23
react
:
18.3.1
react
:
18.3.1
react-dom
:
18.3.1(react@18.3.1)
react-dom
:
18.3.1(react@18.3.1)
...
@@ -4391,13 +4341,13 @@ snapshots:
...
@@ -4391,13 +4341,13 @@ snapshots:
dependencies
:
dependencies
:
'
@uppy/core'
:
2.3.4
'
@uppy/core'
:
2.3.4
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@wangeditor/basic-modules'
:
1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/basic-modules'
:
1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/code-highlight'
:
1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/code-highlight'
:
1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/list-module'
:
1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/list-module'
:
1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/table-module'
:
1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/table-module'
:
1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/upload-image-module'
:
1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/upload-image-module'
:
1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/video-module'
:
1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/video-module'
:
1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
is-hotkey
:
0.2.0
is-hotkey
:
0.2.0
lodash.camelcase
:
4.3.0
lodash.camelcase
:
4.3.0
...
@@ -4407,47 +4357,47 @@ snapshots:
...
@@ -4407,47 +4357,47 @@ snapshots:
lodash.isequal
:
4.5.0
lodash.isequal
:
4.5.0
lodash.throttle
:
4.1.1
lodash.throttle
:
4.1.1
lodash.toarray
:
4.4.0
lodash.toarray
:
4.4.0
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
transitivePeerDependencies
:
transitivePeerDependencies
:
-
preact-render-to-string
-
preact-render-to-string
'
@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
lodash.isequal
:
4.5.0
lodash.isequal
:
4.5.0
lodash.throttle
:
4.1.1
lodash.throttle
:
4.1.1
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@uppy/core'
:
2.3.4
'
@uppy/core'
:
2.3.4
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@wangeditor/basic-modules'
:
1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/basic-modules'
:
1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
lodash.foreach
:
4.5.0
lodash.foreach
:
4.5.0
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
'
@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.16
)(slate@0.72.8)(snabbdom@3.6.4)'
:
'
@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.18
)(slate@0.72.8)(snabbdom@3.6.4)'
:
dependencies
:
dependencies
:
'
@uppy/core'
:
2.3.4
'
@uppy/core'
:
2.3.4
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@uppy/xhr-upload'
:
2.1.3(@uppy/core@2.3.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
6
)(slate@0.72.8)(snabbdom@3.6.4)
'
@wangeditor/core'
:
1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.1
8
)(slate@0.72.8)(snabbdom@3.6.4)
dom7
:
3.0.0
dom7
:
3.0.0
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
slate
:
0.72.8
slate
:
0.72.8
snabbdom
:
3.6.4
snabbdom
:
3.6.4
...
@@ -4458,9 +4408,9 @@ snapshots:
...
@@ -4458,9 +4408,9 @@ snapshots:
address@1.2.2
:
{}
address@1.2.2
:
{}
agent-base@6.0.2
:
agent-base@6.0.2
(supports-color@10.2.2)
:
dependencies
:
dependencies
:
debug
:
4.4.3
debug
:
4.4.3
(supports-color@10.2.2)
transitivePeerDependencies
:
transitivePeerDependencies
:
-
supports-color
-
supports-color
...
@@ -4483,14 +4433,14 @@ snapshots:
...
@@ -4483,14 +4433,14 @@ snapshots:
screenfull
:
5.2.0
screenfull
:
5.2.0
tslib
:
2.8.1
tslib
:
2.8.1
ali-oss@6.23.0
:
ali-oss@6.23.0
(supports-color@10.2.2)
:
dependencies
:
dependencies
:
address
:
1.2.2
address
:
1.2.2
agentkeepalive
:
3.5.3
agentkeepalive
:
3.5.3
bowser
:
1.9.4
bowser
:
1.9.4
copy-to
:
2.0.1
copy-to
:
2.0.1
dateformat
:
2.2.0
dateformat
:
2.2.0
debug
:
4.4.3
debug
:
4.4.3
(supports-color@10.2.2)
destroy
:
1.2.0
destroy
:
1.2.0
end-or-error
:
1.0.1
end-or-error
:
1.0.1
get-ready
:
1.0.0
get-ready
:
1.0.0
...
@@ -4646,11 +4596,11 @@ snapshots:
...
@@ -4646,11 +4596,11 @@ snapshots:
asynckit@0.4.0
:
{}
asynckit@0.4.0
:
{}
axios@1.18.1(debug@4.4.3)
:
axios@1.18.1(debug@4.4.3
(supports-color@10.2.2))(supports-color@10.2.2
)
:
dependencies
:
dependencies
:
follow-redirects
:
1.16.0(debug@4.4.3)
follow-redirects
:
1.16.0(debug@4.4.3
(supports-color@10.2.2)
)
form-data
:
4.0.6
form-data
:
4.0.6
https-proxy-agent
:
5.0.1
https-proxy-agent
:
5.0.1
(supports-color@10.2.2)
proxy-from-env
:
2.1.0
proxy-from-env
:
2.1.0
transitivePeerDependencies
:
transitivePeerDependencies
:
-
debug
-
debug
...
@@ -4746,9 +4696,11 @@ snapshots:
...
@@ -4746,9 +4696,11 @@ snapshots:
dayjs@1.11.21
:
{}
dayjs@1.11.21
:
{}
debug@4.4.3
:
debug@4.4.3
(supports-color@10.2.2)
:
dependencies
:
dependencies
:
ms
:
2.1.3
ms
:
2.1.3
optionalDependencies
:
supports-color
:
10.2.2
decimal.js@10.6.0
:
{}
decimal.js@10.6.0
:
{}
...
@@ -4782,10 +4734,10 @@ snapshots:
...
@@ -4782,10 +4734,10 @@ snapshots:
easy-formula-editor@0.0.2-alpha.1
:
{}
easy-formula-editor@0.0.2-alpha.1
:
{}
echarts@
5.6
.0
:
echarts@
6.1
.0
:
dependencies
:
dependencies
:
tslib
:
2.3.0
tslib
:
2.3.0
zrender
:
5.6.1
zrender
:
6.1.0
ee-first@1.1.1
:
{}
ee-first@1.1.1
:
{}
...
@@ -4915,9 +4867,9 @@ snapshots:
...
@@ -4915,9 +4867,9 @@ snapshots:
file-saver@2.0.5
:
{}
file-saver@2.0.5
:
{}
follow-redirects@1.16.0(debug@4.4.3)
:
follow-redirects@1.16.0(debug@4.4.3
(supports-color@10.2.2)
)
:
optionalDependencies
:
optionalDependencies
:
debug
:
4.4.3
debug
:
4.4.3
(supports-color@10.2.2)
form-data@4.0.6
:
form-data@4.0.6
:
dependencies
:
dependencies
:
...
@@ -4984,10 +4936,10 @@ snapshots:
...
@@ -4984,10 +4936,10 @@ snapshots:
html-void-elements@2.0.1
:
{}
html-void-elements@2.0.1
:
{}
https-proxy-agent@5.0.1
:
https-proxy-agent@5.0.1
(supports-color@10.2.2)
:
dependencies
:
dependencies
:
agent-base
:
6.0.2
agent-base
:
6.0.2
(supports-color@10.2.2)
debug
:
4.4.3
debug
:
4.4.3
(supports-color@10.2.2)
transitivePeerDependencies
:
transitivePeerDependencies
:
-
supports-color
-
supports-color
...
@@ -5101,54 +5053,54 @@ snapshots:
...
@@ -5101,54 +5053,54 @@ snapshots:
needle
:
3.5.0
needle
:
3.5.0
source-map
:
0.6.1
source-map
:
0.6.1
lightningcss-android-arm64@1.3
2
.0
:
lightningcss-android-arm64@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-darwin-arm64@1.3
2
.0
:
lightningcss-darwin-arm64@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-darwin-x64@1.3
2
.0
:
lightningcss-darwin-x64@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-freebsd-x64@1.3
2
.0
:
lightningcss-freebsd-x64@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-linux-arm-gnueabihf@1.3
2
.0
:
lightningcss-linux-arm-gnueabihf@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-linux-arm64-gnu@1.3
2
.0
:
lightningcss-linux-arm64-gnu@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-linux-arm64-musl@1.3
2
.0
:
lightningcss-linux-arm64-musl@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-linux-x64-gnu@1.3
2
.0
:
lightningcss-linux-x64-gnu@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-linux-x64-musl@1.3
2
.0
:
lightningcss-linux-x64-musl@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-win32-arm64-msvc@1.3
2
.0
:
lightningcss-win32-arm64-msvc@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss-win32-x64-msvc@1.3
2
.0
:
lightningcss-win32-x64-msvc@1.3
3
.0
:
optional
:
true
optional
:
true
lightningcss@1.3
2
.0
:
lightningcss@1.3
3
.0
:
dependencies
:
dependencies
:
detect-libc
:
2.1.2
detect-libc
:
2.1.2
optionalDependencies
:
optionalDependencies
:
lightningcss-android-arm64
:
1.3
2
.0
lightningcss-android-arm64
:
1.3
3
.0
lightningcss-darwin-arm64
:
1.3
2
.0
lightningcss-darwin-arm64
:
1.3
3
.0
lightningcss-darwin-x64
:
1.3
2
.0
lightningcss-darwin-x64
:
1.3
3
.0
lightningcss-freebsd-x64
:
1.3
2
.0
lightningcss-freebsd-x64
:
1.3
3
.0
lightningcss-linux-arm-gnueabihf
:
1.3
2
.0
lightningcss-linux-arm-gnueabihf
:
1.3
3
.0
lightningcss-linux-arm64-gnu
:
1.3
2
.0
lightningcss-linux-arm64-gnu
:
1.3
3
.0
lightningcss-linux-arm64-musl
:
1.3
2
.0
lightningcss-linux-arm64-musl
:
1.3
3
.0
lightningcss-linux-x64-gnu
:
1.3
2
.0
lightningcss-linux-x64-gnu
:
1.3
3
.0
lightningcss-linux-x64-musl
:
1.3
2
.0
lightningcss-linux-x64-musl
:
1.3
3
.0
lightningcss-win32-arm64-msvc
:
1.3
2
.0
lightningcss-win32-arm64-msvc
:
1.3
3
.0
lightningcss-win32-x64-msvc
:
1.3
2
.0
lightningcss-win32-x64-msvc
:
1.3
3
.0
linkify-it@5.0.2
:
linkify-it@5.0.2
:
dependencies
:
dependencies
:
...
@@ -5254,7 +5206,7 @@ snapshots:
...
@@ -5254,7 +5206,7 @@ snapshots:
stacktrace-js
:
2.0.2
stacktrace-js
:
2.0.2
stylis
:
4.4.0
stylis
:
4.4.0
nanoid@3.3.1
6
:
{}
nanoid@3.3.1
8
:
{}
needle@3.5.0
:
needle@3.5.0
:
dependencies
:
dependencies
:
...
@@ -5266,13 +5218,13 @@ snapshots:
...
@@ -5266,13 +5218,13 @@ snapshots:
node-hex@1.0.1
:
{}
node-hex@1.0.1
:
{}
nuqs@2.9.0(react-router-dom@7.18.
1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
:
nuqs@2.9.0(react-router-dom@7.18.
2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
:
dependencies
:
dependencies
:
'
@standard-schema/spec'
:
1.0.0
'
@standard-schema/spec'
:
1.0.0
react
:
19.2.7
react
:
19.2.7
optionalDependencies
:
optionalDependencies
:
react-router
:
7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-router
:
7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-router-dom
:
7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-router-dom
:
7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
object-assign@4.1.1
:
{}
object-assign@4.1.1
:
{}
...
@@ -5357,9 +5309,9 @@ snapshots:
...
@@ -5357,9 +5309,9 @@ snapshots:
platform@1.3.6
:
{}
platform@1.3.6
:
{}
postcss@8.5.
19
:
postcss@8.5.
26
:
dependencies
:
dependencies
:
nanoid
:
3.3.1
6
nanoid
:
3.3.1
8
picocolors
:
1.1.1
picocolors
:
1.1.1
source-map-js
:
1.2.1
source-map-js
:
1.2.1
...
@@ -5737,14 +5689,14 @@ snapshots:
...
@@ -5737,14 +5689,14 @@ snapshots:
react-is@19.2.7
:
{}
react-is@19.2.7
:
{}
react-router-dom@7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
:
react-router-dom@7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
:
dependencies
:
dependencies
:
react
:
19.2.7
react
:
19.2.7
react-dom
:
19.2.7(react@19.2.7)
react-dom
:
19.2.7(react@19.2.7)
react-router
:
7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-router
:
7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
optional
:
true
optional
:
true
react-router@7.18.
1
(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
:
react-router@7.18.
2
(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
:
dependencies
:
dependencies
:
cookie
:
1.1.1
cookie
:
1.1.1
react
:
18.3.1
react
:
18.3.1
...
@@ -5752,7 +5704,7 @@ snapshots:
...
@@ -5752,7 +5704,7 @@ snapshots:
optionalDependencies
:
optionalDependencies
:
react-dom
:
18.3.1(react@18.3.1)
react-dom
:
18.3.1(react@18.3.1)
react-router@7.18.
1
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
:
react-router@7.18.
2
(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
:
dependencies
:
dependencies
:
cookie
:
1.1.1
cookie
:
1.1.1
react
:
19.2.7
react
:
19.2.7
...
@@ -5809,26 +5761,25 @@ snapshots:
...
@@ -5809,26 +5761,25 @@ snapshots:
resize-observer-polyfill@1.5.1
:
{}
resize-observer-polyfill@1.5.1
:
{}
rolldown@1.
1.5
:
rolldown@1.
2.4
:
dependencies
:
dependencies
:
'
@oxc-project/types'
:
0.1
39
.0
'
@oxc-project/types'
:
0.1
44
.0
'
@rolldown/pluginutils'
:
1.0.1
'
@rolldown/pluginutils'
:
1.0.1
optionalDependencies
:
optionalDependencies
:
'
@rolldown/binding-android-arm64'
:
1.1.5
'
@rolldown/binding-android-arm64'
:
1.2.4
'
@rolldown/binding-darwin-arm64'
:
1.1.5
'
@rolldown/binding-darwin-arm64'
:
1.2.4
'
@rolldown/binding-darwin-x64'
:
1.1.5
'
@rolldown/binding-darwin-x64'
:
1.2.4
'
@rolldown/binding-freebsd-x64'
:
1.1.5
'
@rolldown/binding-freebsd-x64'
:
1.2.4
'
@rolldown/binding-linux-arm-gnueabihf'
:
1.1.5
'
@rolldown/binding-linux-arm-gnueabihf'
:
1.2.4
'
@rolldown/binding-linux-arm64-gnu'
:
1.1.5
'
@rolldown/binding-linux-arm64-gnu'
:
1.2.4
'
@rolldown/binding-linux-arm64-musl'
:
1.1.5
'
@rolldown/binding-linux-arm64-musl'
:
1.2.4
'
@rolldown/binding-linux-ppc64-gnu'
:
1.1.5
'
@rolldown/binding-linux-ppc64-gnu'
:
1.2.4
'
@rolldown/binding-linux-s390x-gnu'
:
1.1.5
'
@rolldown/binding-linux-s390x-gnu'
:
1.2.4
'
@rolldown/binding-linux-x64-gnu'
:
1.1.5
'
@rolldown/binding-linux-x64-gnu'
:
1.2.4
'
@rolldown/binding-linux-x64-musl'
:
1.1.5
'
@rolldown/binding-linux-x64-musl'
:
1.2.4
'
@rolldown/binding-openharmony-arm64'
:
1.1.5
'
@rolldown/binding-openharmony-arm64'
:
1.2.4
'
@rolldown/binding-wasm32-wasi'
:
1.1.5
'
@rolldown/binding-win32-arm64-msvc'
:
1.2.4
'
@rolldown/binding-win32-arm64-msvc'
:
1.1.5
'
@rolldown/binding-win32-x64-msvc'
:
1.2.4
'
@rolldown/binding-win32-x64-msvc'
:
1.1.5
rtl-css-js@1.16.1
:
rtl-css-js@1.16.1
:
dependencies
:
dependencies
:
...
@@ -5972,6 +5923,8 @@ snapshots:
...
@@ -5972,6 +5923,8 @@ snapshots:
stylis@4.4.0
:
{}
stylis@4.4.0
:
{}
supports-color@10.2.2
:
{}
symbol-tree@3.2.4
:
{}
symbol-tree@3.2.4
:
{}
terser@5.49.0
:
terser@5.49.0
:
...
@@ -6066,6 +6019,8 @@ snapshots:
...
@@ -6066,6 +6019,8 @@ snapshots:
undici@7.28.0
:
{}
undici@7.28.0
:
{}
undici@8.10.0
:
{}
unescape@1.0.1
:
unescape@1.0.1
:
dependencies
:
dependencies
:
extend-shallow
:
2.0.1
extend-shallow
:
2.0.1
...
@@ -6104,20 +6059,19 @@ snapshots:
...
@@ -6104,20 +6059,19 @@ snapshots:
mz
:
2.7.0
mz
:
2.7.0
unescape
:
1.0.1
unescape
:
1.0.1
vite-plugin-mkcert@
1.17.12(vite@8.1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
:
vite-plugin-mkcert@
2.1.0(vite@8.2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
:
dependencies
:
dependencies
:
debug
:
4.4.3
debug
:
4.4.3(supports-color@10.2.2)
picocolors
:
1.1.1
supports-color
:
10.2.2
vite
:
8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
undici
:
8.10.0
transitivePeerDependencies
:
vite
:
8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
-
supports-color
vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
:
vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
:
dependencies
:
dependencies
:
lightningcss
:
1.3
2
.0
lightningcss
:
1.3
3
.0
picomatch
:
4.0.5
picomatch
:
4.0.5
postcss
:
8.5.
19
postcss
:
8.5.
26
rolldown
:
1.
1.5
rolldown
:
1.
2.4
tinyglobby
:
0.2.17
tinyglobby
:
0.2.17
optionalDependencies
:
optionalDependencies
:
'
@types/node'
:
22.20.1
'
@types/node'
:
22.20.1
...
@@ -6126,10 +6080,10 @@ snapshots:
...
@@ -6126,10 +6080,10 @@ snapshots:
less
:
4.6.7
less
:
4.6.7
terser
:
5.49.0
terser
:
5.49.0
vitest@4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
:
vitest@4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
:
dependencies
:
dependencies
:
'
@vitest/expect'
:
4.1.10
'
@vitest/expect'
:
4.1.10
'
@vitest/mocker'
:
4.1.10(vite@8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
'
@vitest/mocker'
:
4.1.10(vite@8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
'
@vitest/pretty-format'
:
4.1.10
'
@vitest/pretty-format'
:
4.1.10
'
@vitest/runner'
:
4.1.10
'
@vitest/runner'
:
4.1.10
'
@vitest/snapshot'
:
4.1.10
'
@vitest/snapshot'
:
4.1.10
...
@@ -6146,7 +6100,7 @@ snapshots:
...
@@ -6146,7 +6100,7 @@ snapshots:
tinyexec
:
1.2.4
tinyexec
:
1.2.4
tinyglobby
:
0.2.17
tinyglobby
:
0.2.17
tinyrainbow
:
3.1.0
tinyrainbow
:
3.1.0
vite
:
8.
1.5
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite
:
8.
2.1
(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
why-is-node-running
:
2.3.0
why-is-node-running
:
2.3.0
optionalDependencies
:
optionalDependencies
:
'
@types/node'
:
22.20.1
'
@types/node'
:
22.20.1
...
@@ -6196,7 +6150,7 @@ snapshots:
...
@@ -6196,7 +6150,7 @@ snapshots:
xtend@4.0.2
:
{}
xtend@4.0.2
:
{}
zrender@
5.6.1
:
zrender@
6.1.0
:
dependencies
:
dependencies
:
tslib
:
2.3.0
tslib
:
2.3.0
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论