feat(directory): finish voice, vcs, operator logo, subtitle
This commit is contained in:
@@ -1,6 +1,51 @@
|
|||||||
showcase:
|
showcase:
|
||||||
2023/03/01:
|
2023/02/26:
|
||||||
- Under Construction :)
|
- Rename w_fugue to w_wonder
|
||||||
|
2023/02/14:
|
||||||
|
- Added pozemka_snowy_plains_in_words
|
||||||
|
2023/02/11:
|
||||||
|
- Added Insights
|
||||||
|
- Added Operator Voice
|
||||||
|
2023/02/05:
|
||||||
|
- Added ability to move Operator Logo around
|
||||||
|
- Added VCs
|
||||||
|
2023/01/17:
|
||||||
|
- Added ling, chongyue
|
||||||
|
2023/01/16:
|
||||||
|
- Refactor the project to use nodeJS
|
||||||
|
2022/12/01:
|
||||||
|
- Added mizuki_summer_feast
|
||||||
|
2022/10/31:
|
||||||
|
- Added texas, relight/neral, become anew/rosmon, dream/passager
|
||||||
|
2022/10/24:
|
||||||
|
- Added lee_trust_your_eyes
|
||||||
|
2022/08/11:
|
||||||
|
- Added gavial, surtr#9
|
||||||
|
2022/05/01:
|
||||||
|
- Added w_fugue, specter, and skadi_sublimation
|
||||||
|
2022/03/29:
|
||||||
|
- Moved to Spine 3.8.99
|
||||||
|
- Added phatom
|
||||||
|
2022/01/23:
|
||||||
|
- Added dusk, ling, everything is a miracle/dusk, unfettered freedom/nian
|
||||||
|
2021/11/01:
|
||||||
|
- Added Nearl, W, and Rosmontis
|
||||||
|
2021/08/21:
|
||||||
|
- Updated skadi
|
||||||
|
- Added nian, chen
|
||||||
|
2021/05/26:
|
||||||
|
- First commit
|
||||||
directory:
|
directory:
|
||||||
|
2023/03/02:
|
||||||
|
- Added Voice, VCs, Operator Logo
|
||||||
2023/03/01:
|
2023/03/01:
|
||||||
- Under Construction :)
|
- Added Operator page
|
||||||
|
- Added Changelogs page
|
||||||
|
2023/02/28:
|
||||||
|
- Init Operator page
|
||||||
|
2023/02/26:
|
||||||
|
- Added Home page
|
||||||
|
2023/02/25:
|
||||||
|
- Added portrait images
|
||||||
|
2023/02/21:
|
||||||
|
- Init Directory
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
VITE_APP_TITLE=AKLive2D
|
VITE_APP_TITLE=AKLive2D
|
||||||
VITE_APP_VOICE_URL=jp/CN_037.ogg
|
VITE_APP_VOICE_URL=jp/CN_037.ogg
|
||||||
VITE_VOICE_FOLDERS={"main":"voice","sub":[{"name":"jp","lang":"JP"},{"name":"cn","lang":"CN_MANDARIN"},{"name":"en","lang":"EN"},{"name":"kr","lang":"KR"},{"name":"custom","lang":"CUSTOM"}]}
|
VITE_VOICE_FOLDERS={"main":"voice","sub":[{"name":"jp","lang":"JP"},{"name":"cn","lang":"CN_MANDARIN"},{"name":"en","lang":"EN"},{"name":"kr","lang":"KR"},{"name":"custom","lang":"CUSTOM"}]}
|
||||||
|
VITE_DIRECTORY_FOLDER="_assets"
|
||||||
|
VITE_BACKGROUND_FOLDER="background"
|
||||||
@@ -1 +1 @@
|
|||||||
0.5.24
|
0.5.28
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup .return-button {
|
.popup .return-button {
|
||||||
color: #666;
|
color: var(--button-color);
|
||||||
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,34 @@
|
|||||||
"zh-CN": "中文方言",
|
"zh-CN": "中文方言",
|
||||||
"en-US": "Chinese Topolect"
|
"en-US": "Chinese Topolect"
|
||||||
},
|
},
|
||||||
|
"steam_workshop": {
|
||||||
|
"zh-CN": "壁纸引擎版",
|
||||||
|
"en-US": "Wallpaper Engine Version"
|
||||||
|
},
|
||||||
|
"external_links": {
|
||||||
|
"zh-CN": "外部链接",
|
||||||
|
"en-US": "External Links"
|
||||||
|
},
|
||||||
|
"web_version": {
|
||||||
|
"zh-CN": "全功能网页版",
|
||||||
|
"en-US": "Full Feature Web Version"
|
||||||
|
},
|
||||||
|
"idle": {
|
||||||
|
"zh-CN": "待机",
|
||||||
|
"en-US": "Idle"
|
||||||
|
},
|
||||||
|
"interact": {
|
||||||
|
"zh-CN": "交互",
|
||||||
|
"en-US": "Interact"
|
||||||
|
},
|
||||||
|
"special": {
|
||||||
|
"zh-CN": "特殊",
|
||||||
|
"en-US": "Special"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"zh-CN": "字幕",
|
||||||
|
"en-US": "Subtitle"
|
||||||
|
},
|
||||||
"zh-CN": {
|
"zh-CN": {
|
||||||
"zh-CN": "简体中文",
|
"zh-CN": "简体中文",
|
||||||
"en-US": "Chinese (Simplified)"
|
"en-US": "Chinese (Simplified)"
|
||||||
@@ -98,6 +126,18 @@
|
|||||||
"en-US": {
|
"en-US": {
|
||||||
"zh-CN": "英语",
|
"zh-CN": "英语",
|
||||||
"en-US": "English"
|
"en-US": "English"
|
||||||
|
},
|
||||||
|
"zh-TW": {
|
||||||
|
"zh-CN": "繁体中文",
|
||||||
|
"en-US": "Chinese (Traditional)"
|
||||||
|
},
|
||||||
|
"ja-JP": {
|
||||||
|
"zh-CN": "日语",
|
||||||
|
"en-US": "Japanese"
|
||||||
|
},
|
||||||
|
"ko-KR": {
|
||||||
|
"zh-CN": "韩语",
|
||||||
|
"en-US": "Korean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.item-group .item .item-info .item-title-container {
|
.item-group .item .item-info .item-title-container {
|
||||||
color: white;
|
color: var(--text-color-full);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default function Home() {
|
|||||||
const isShown = useCallback((type) => currentTab === 'all' || currentTab === type, [currentTab])
|
const isShown = useCallback((type) => currentTab === 'all' || currentTab === type, [currentTab])
|
||||||
|
|
||||||
const playVoice = useCallback((link) => {
|
const playVoice = useCallback((link) => {
|
||||||
const audioUrl = `/${link}/assets/voice/${import.meta.env.VITE_APP_VOICE_URL}`
|
const audioUrl = `/${link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`
|
||||||
if (!voiceOn || (audioEl.src === (window.location.href.replace(/\/$/g, '') + audioUrl) && isPlaying)) return
|
if (!voiceOn || (audioEl.src === (window.location.href.replace(/\/$/g, '') + audioUrl) && isPlaying)) return
|
||||||
audioEl.src = audioUrl
|
audioEl.src = audioUrl
|
||||||
let startPlayPromise = audioEl.play()
|
let startPlayPromise = audioEl.play()
|
||||||
|
|||||||
@@ -1,144 +1,242 @@
|
|||||||
.operator .spine-player-wrapper {
|
.operator .spine-player-wrapper {
|
||||||
padding: 3rem 0;
|
padding: 3rem 0 2rem 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings {
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .text {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-title-wrapper {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
border-left: 3px solid currentColor;
|
|
||||||
padding-left: 0.75rem;
|
|
||||||
margin-bottom: 0.81rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper {
|
|
||||||
margin-bottom: 0.81rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content {
|
|
||||||
padding: 0.81rem 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text {
|
|
||||||
pointer-events: none;
|
|
||||||
position: relative;
|
|
||||||
transform: translate3d(0, 0, 1px);
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 0.44rem 2.25rem 0.44rem 0.63rem;
|
|
||||||
background-color: rgba(67, 67, 67, 0.3);
|
|
||||||
background-image: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0, rgba(255, 255, 255, 0.1) 1px, transparent 1px, transparent 4px);
|
|
||||||
transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content:hover,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content.active {
|
|
||||||
transform: translate3d(6px, 0, 1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: content-box;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
padding: 6px;
|
|
||||||
border: rgba(214, 214, 214, 0.3) 1px dashed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text::before,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
transition: opacity 0.3s, visibility 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline::before,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline::after {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
left: -2px;
|
|
||||||
height: 2px;
|
|
||||||
width: 100%;
|
|
||||||
border-left: var(--text-color) solid 2px;
|
|
||||||
border-right: var(--text-color) solid 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline::before {
|
|
||||||
top: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .outline::after {
|
|
||||||
bottom: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text::before {
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 60%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: linear-gradient(90deg, transparent, currentColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content:hover .content-text::before,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content.active .content-text::before,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content:hover .content-text .outline,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content.active .content-text .outline,
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content.active .content-text .tick-icon {
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .spine-settings .settings-content-wrapper .content .content-text .tick-icon {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 0;
|
|
||||||
right: 0.31rem;
|
|
||||||
top: 50%;
|
|
||||||
width: 0.5rem;
|
|
||||||
height: 1rem;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
transition: opacity 0.3s, visibility 0.3s;
|
|
||||||
border-right: var(--text-color) solid 0.25rem;
|
|
||||||
border-bottom: var(--text-color) solid 0.25rem;
|
|
||||||
transform: translate(-50%, -70%) rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .backgrounds-dropdown {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .backgrounds-dropdown .content {
|
|
||||||
padding-right: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.operator .backgrounds-dropdown .icon {
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.operator .backgrounds-dropdown .menu {
|
|
||||||
left: 0;
|
|
||||||
right: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.operator .spine-container {
|
.operator .spine-container {
|
||||||
max-height: 720px;
|
background-position: center;
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operator .spine-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.operator .spine-container:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
padding-top: 100%;
|
||||||
|
}
|
||||||
|
.operator .spine-container .spine-player {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .spine-settings,
|
||||||
|
.operator .steam-workshop-wrapper {
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .text {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .spine-settings .settings-title-wrapper {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
border-left: 3px solid currentColor;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content {
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text {
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
transform: translate3d(0, 0, 1px);
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0.44rem 2.25rem 0.44rem 0.63rem;
|
||||||
|
background-color: var(--home-item-hover-background-color);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--home-item-background-linear-gradient-color) 0,
|
||||||
|
var(--home-item-background-linear-gradient-color) 1px,
|
||||||
|
transparent 1px,
|
||||||
|
transparent 4px
|
||||||
|
);
|
||||||
|
transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content:hover,
|
||||||
|
.operator .styled-selection .content.active {
|
||||||
|
transform: translate3d(6px, 0, 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text .outline {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: -6px;
|
||||||
|
top: -6px;
|
||||||
|
border: var(--home-item-outline-color) 1px dashed;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text::before,
|
||||||
|
.operator .styled-selection .content .content-text .outline {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s,
|
||||||
|
visibility cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text .outline::before,
|
||||||
|
.operator .styled-selection .content .content-text .outline::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -2px;
|
||||||
|
height: 2px;
|
||||||
|
width: 100%;
|
||||||
|
border-left: var(--text-color) solid 2px;
|
||||||
|
border-right: var(--text-color) solid 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text .outline::before {
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text .outline::after {
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text::before {
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 60%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: linear-gradient(90deg, transparent, currentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content:hover .content-text::before,
|
||||||
|
.operator .styled-selection .content.active .content-text::before,
|
||||||
|
.operator .styled-selection .content:hover .content-text .outline,
|
||||||
|
.operator .styled-selection .content.active .content-text .outline,
|
||||||
|
.operator .styled-selection .content.active .content-text .tick-icon {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .styled-selection .content .content-text .tick-icon {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
right: 0.31rem;
|
||||||
|
top: 50%;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s,
|
||||||
|
visibility cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
border-right: var(--text-color) solid 0.25rem;
|
||||||
|
border-bottom: var(--text-color) solid 0.25rem;
|
||||||
|
transform: translate(-50%, -70%) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .voice-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 30%;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 480px;
|
||||||
|
width: 85%;
|
||||||
|
opacity: 0;
|
||||||
|
margin: 16px;
|
||||||
|
transition: all 0.5s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||||
|
visibility: hidden;
|
||||||
|
font-family: 'Noto Sans', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .voice-wrapper.active {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .voice-wrapper .voice-title {
|
||||||
|
background-color: #9e9e9e;
|
||||||
|
color: #000;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: -12px;
|
||||||
|
left: -8px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
max-width: 180px;
|
||||||
|
width: 65%;
|
||||||
|
box-shadow: 0 3px 6px #00000080;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .voice-wrapper .voice-subtitle {
|
||||||
|
background-color: #000000a6;
|
||||||
|
color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
box-shadow: 0 6px 12px #00000080;
|
||||||
|
position: relative;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .voice-wrapper .voice-triangle {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 8px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 8px 8px 8px 8px;
|
||||||
|
border-color: white transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operator .operator-logo {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 30%;
|
||||||
|
height: auto;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.operator .spine-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.operator .spine-container:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
padding-top: 100%;
|
||||||
|
}
|
||||||
|
.operator .spine-container .spine-player {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.operator .spine-player-wrapper {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
.operator .voice-wrapper {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,12 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
|
useCallback
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
useParams,
|
useParams,
|
||||||
useNavigate
|
useNavigate,
|
||||||
|
Link
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { atom, useAtom } from 'jotai';
|
import { atom, useAtom } from 'jotai';
|
||||||
import './operator.css'
|
import './operator.css'
|
||||||
@@ -20,11 +22,11 @@ import spine from '!/libs/spine-player'
|
|||||||
import '!/libs/spine-player.css'
|
import '!/libs/spine-player.css'
|
||||||
import MainBorder from '@/component/main_border';
|
import MainBorder from '@/component/main_border';
|
||||||
import { useI18n } from '@/state/language';
|
import { useI18n } from '@/state/language';
|
||||||
import Dropdown from '@/component/dropdown';
|
|
||||||
|
|
||||||
const getVoiceFoler = (lang) => {
|
const getVoiceFoler = (lang) => {
|
||||||
const folderObject = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
|
const folderObject = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
|
||||||
return `${folderObject.main}/${folderObject.sub.find(e => e.lang === lang).name}`
|
const voiceFolder = folderObject.sub.find(e => e.lang === lang) || folderObject.sub.find(e => e.name === 'custom')
|
||||||
|
return `${folderObject.main}/${voiceFolder.name}`
|
||||||
}
|
}
|
||||||
const configAtom = atom(null);
|
const configAtom = atom(null);
|
||||||
const spinePlayerAtom = atom(null);
|
const spinePlayerAtom = atom(null);
|
||||||
@@ -44,11 +46,20 @@ export default function Operator(props) {
|
|||||||
} = useHeader()
|
} = useHeader()
|
||||||
const [config, setConfig] = useAtom(configAtom)
|
const [config, setConfig] = useAtom(configAtom)
|
||||||
const [spineData, setSpineData] = useState(null)
|
const [spineData, setSpineData] = useState(null)
|
||||||
const _trackEvt = useUmami(`/operator/${key}`)
|
const _trackEvt = useUmami(`/${key}`)
|
||||||
const spineRef = useRef(null)
|
const spineRef = useRef(null)
|
||||||
const [, setSpinePlayer] = useAtom(spinePlayerAtom)
|
const [spineAnimation, setSpineAnimation] = useAtom(spineAnimationAtom)
|
||||||
const { currentBackground } = useBackgrounds()
|
const { i18n } = useI18n()
|
||||||
const [spineAnimation,] = useAtom(spineAnimationAtom)
|
const [spinePlayer, setSpinePlayer] = useAtom(spinePlayerAtom)
|
||||||
|
const [voiceLang, setVoiceLang] = useState(null)
|
||||||
|
const { backgrounds, currentBackground, setCurrentBackground } = useBackgrounds()
|
||||||
|
const [voiceConfig, setVoiceConfig] = useState(null)
|
||||||
|
const [subtitleLang, setSubtitleLang] = useState(null)
|
||||||
|
const [subtitle, setSubtitle] = useState(null)
|
||||||
|
const [hideSubtitle, setHideSubtitle] = useState(true)
|
||||||
|
const [isVoicePlaying, setIsVoicePlaying] = useState(false)
|
||||||
|
const [lastVoiceId, setLastVoiceId] = useState(null)
|
||||||
|
const [currentVoiceId, setCurrentVoiceId] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAppbarExtraArea([])
|
setAppbarExtraArea([])
|
||||||
@@ -59,13 +70,16 @@ export default function Operator(props) {
|
|||||||
const config = operators.find((item) => item.link === key)
|
const config = operators.find((item) => item.link === key)
|
||||||
if (config) {
|
if (config) {
|
||||||
setConfig(config)
|
setConfig(config)
|
||||||
fetch(`/_assets/${config.filename.replace("#", "%23")}.json`).then(res => res.json()).then(data => {
|
fetch(`/${import.meta.env.VITE_DIRECTORY_FOLDER}/${config.filename.replace("#", "%23")}.json`).then(res => res.json()).then(data => {
|
||||||
setSpineData(data)
|
setSpineData(data)
|
||||||
})
|
})
|
||||||
setHeaderIcon(config.type)
|
setHeaderIcon(config.type)
|
||||||
if (spineRef.current?.children.length > 0) {
|
if (spineRef.current?.children.length > 0) {
|
||||||
spineRef.current?.removeChild(spineRef.current?.children[0])
|
spineRef.current?.removeChild(spineRef.current?.children[0])
|
||||||
}
|
}
|
||||||
|
fetch(`/${import.meta.env.VITE_DIRECTORY_FOLDER}/voice_${config.link}.json`).then(res => res.json()).then(data => {
|
||||||
|
setVoiceConfig(data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [operators, key])
|
}, [operators, key])
|
||||||
|
|
||||||
@@ -137,41 +151,77 @@ export default function Operator(props) {
|
|||||||
touch: false,
|
touch: false,
|
||||||
fps: 60,
|
fps: 60,
|
||||||
defaultMix: 0,
|
defaultMix: 0,
|
||||||
success: (spinePlayer) => {
|
|
||||||
spinePlayer.setAnimation(spineAnimation, true)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}, [spineData]);
|
}, [spineData]);
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<section className="operator">
|
if (voiceConfig && voiceLang) {
|
||||||
<section className="spine-player-wrapper">
|
let subtitleObj = voiceConfig.subtitleLangs[subtitleLang || 'zh-CN']
|
||||||
<SpineSettingsElement />
|
let subtitleKey = 'default'
|
||||||
<section className="spine-container" ref={spineRef} style={{
|
if (subtitleObj[voiceLang]) {
|
||||||
backgroundImage: `url(/${key}/assets/background/${currentBackground})`
|
subtitleKey = voiceLang
|
||||||
}} />
|
}
|
||||||
</section>
|
subtitleObj = subtitleObj[subtitleKey]
|
||||||
<MainBorder />
|
const playVoice = () => {
|
||||||
</section>
|
const voiceId = () => {
|
||||||
)
|
const keys = Object.keys(subtitleObj)
|
||||||
}
|
const id = keys[Math.floor((Math.random() * keys.length))]
|
||||||
|
return id === lastVoiceId ? voiceId() : id
|
||||||
|
}
|
||||||
|
const id = voiceId()
|
||||||
|
setLastVoiceId(id)
|
||||||
|
setCurrentVoiceId(id)
|
||||||
|
audioEl.src = `/${config.link}/assets/${getVoiceFoler(voiceLang)}/${id}.ogg`
|
||||||
|
let startPlayPromise = audioEl.play()
|
||||||
|
if (startPlayPromise !== undefined) {
|
||||||
|
startPlayPromise
|
||||||
|
.then(() => {
|
||||||
|
setIsVoicePlaying(true)
|
||||||
|
const audioEndedFunc = () => {
|
||||||
|
setIsVoicePlaying(false)
|
||||||
|
audioEl.removeEventListener('ended', audioEndedFunc)
|
||||||
|
if (currentVoiceId !== id) return
|
||||||
|
setSubtitle(null)
|
||||||
|
}
|
||||||
|
if (subtitleLang) showSubtitle(id)
|
||||||
|
audioEl.addEventListener('ended', audioEndedFunc)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const showSubtitle = (id) => {
|
||||||
|
setSubtitle(subtitleObj[id])
|
||||||
|
setHideSubtitle(false)
|
||||||
|
}
|
||||||
|
spineRef.current?.addEventListener('click', playVoice)
|
||||||
|
return () => {
|
||||||
|
spineRef.current?.removeEventListener('click', playVoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [voiceLang, spineRef, voiceConfig, subtitleLang, lastVoiceId])
|
||||||
|
|
||||||
function SpineSettingsElement() {
|
|
||||||
const [config,] = useAtom(configAtom)
|
|
||||||
const { i18n } = useI18n()
|
|
||||||
const [spineAnimation, setSpineAnimation] = useAtom(spineAnimationAtom)
|
|
||||||
const [spinePlayer,] = useAtom(spinePlayerAtom)
|
|
||||||
const [voiceLang, setVoiceLang] = useState(null)
|
|
||||||
|
|
||||||
const { backgrounds, currentBackground, setCurrentBackground } = useBackgrounds()
|
useEffect(() => {
|
||||||
|
if (!isVoicePlaying && !hideSubtitle) {
|
||||||
|
const hideSubtitle = () => {
|
||||||
|
setHideSubtitle(true)
|
||||||
|
}
|
||||||
|
setTimeout(hideSubtitle, 5 * 1000)
|
||||||
|
return () => {
|
||||||
|
clearTimeout(hideSubtitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isVoicePlaying, hideSubtitle])
|
||||||
|
|
||||||
const spineSettings = [
|
const spineSettings = [
|
||||||
{
|
{
|
||||||
name: 'animation',
|
name: 'animation',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Idle',
|
name: 'idle',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
spinePlayer.setAnimation("Idle", true)
|
spinePlayer.setAnimation("Idle", true)
|
||||||
setSpineAnimation('Idle')
|
setSpineAnimation('Idle')
|
||||||
@@ -180,7 +230,7 @@ function SpineSettingsElement() {
|
|||||||
return spineAnimation === 'Idle'
|
return spineAnimation === 'Idle'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: 'Interact',
|
name: 'interact',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
spinePlayer.setAnimation("Interact", true)
|
spinePlayer.setAnimation("Interact", true)
|
||||||
setSpineAnimation('Interact')
|
setSpineAnimation('Interact')
|
||||||
@@ -189,7 +239,7 @@ function SpineSettingsElement() {
|
|||||||
return spineAnimation === 'Interact'
|
return spineAnimation === 'Interact'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: 'Special',
|
name: 'special',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
spinePlayer.setAnimation("Special", true)
|
spinePlayer.setAnimation("Special", true)
|
||||||
setSpineAnimation('Special')
|
setSpineAnimation('Special')
|
||||||
@@ -201,7 +251,7 @@ function SpineSettingsElement() {
|
|||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
name: 'voice',
|
name: 'voice',
|
||||||
options: config?.voiceLangs.map((item) => {
|
options: voiceConfig && Object.keys(voiceConfig?.voiceLangs["zh-CN"]).map((item) => {
|
||||||
return {
|
return {
|
||||||
name: i18n(item),
|
name: i18n(item),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -216,61 +266,140 @@ function SpineSettingsElement() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) || []
|
}) || []
|
||||||
|
}, {
|
||||||
|
name: 'subtitle',
|
||||||
|
options: voiceConfig && Object.keys(voiceConfig?.subtitleLangs).map((item) => {
|
||||||
|
return {
|
||||||
|
name: i18n(item),
|
||||||
|
onClick: () => {
|
||||||
|
if (subtitleLang !== item) {
|
||||||
|
setSubtitleLang(item)
|
||||||
|
} else {
|
||||||
|
setSubtitleLang(null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
activeRule: () => {
|
||||||
|
return subtitleLang === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
}, {
|
||||||
|
name: 'backgrounds',
|
||||||
|
options: backgrounds.map((item) => {
|
||||||
|
return {
|
||||||
|
name: item,
|
||||||
|
onClick: () => {
|
||||||
|
setCurrentBackground(item)
|
||||||
|
},
|
||||||
|
activeRule: () => {
|
||||||
|
return currentBackground === item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="spine-settings" style={{
|
<section className="operator">
|
||||||
color: config?.color
|
<section className="spine-player-wrapper">
|
||||||
}}>
|
<section className="spine-settings" style={{
|
||||||
{
|
color: config?.color
|
||||||
spineSettings.map((item) => {
|
}}>
|
||||||
if (item.options.length === 0) return null
|
{
|
||||||
return (
|
spineSettings.map((item) => {
|
||||||
<section key={item.name}>
|
if (item.options.length === 0) return null
|
||||||
<section className='settings-title-wrapper'>
|
return (
|
||||||
<section className='text'>{i18n(item.name)}</section>
|
<section key={item.name}>
|
||||||
</section>
|
<section className='settings-title-wrapper'>
|
||||||
<section className='settings-content-wrapper'>
|
<section className='text'>{i18n(item.name)}</section>
|
||||||
{item.options.map((option) => {
|
</section>
|
||||||
return (
|
<section className='settings-content-wrapper styled-selection'>
|
||||||
<section className={`content ${option.activeRule && option.activeRule() ? 'active' : ''}`} onClick={() => option.onClick()} key={option.name}>
|
{item.options.map((option) => {
|
||||||
|
return (
|
||||||
|
<section className={`content ${option.activeRule && option.activeRule() ? 'active' : ''}`} onClick={(e) => option.onClick(e)} key={option.name}>
|
||||||
|
<section className='content-text'>
|
||||||
|
<section className="outline" />
|
||||||
|
<section className='text'>{i18n(option.name)}</section>
|
||||||
|
<section className='tick-icon' />
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<section>
|
||||||
|
<section className='settings-title-wrapper'>
|
||||||
|
<section className='text'>{i18n('external_links')}</section>
|
||||||
|
</section>
|
||||||
|
<section className='settings-content-wrapper styled-selection'>
|
||||||
|
<Link
|
||||||
|
reloadDocument
|
||||||
|
to={`./index.html?settings`}
|
||||||
|
target='_blank'
|
||||||
|
className='extra-links-item'
|
||||||
|
style={{
|
||||||
|
color: config?.color
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<section className='content'>
|
||||||
|
<section className='content-text'>
|
||||||
|
<section className="outline" />
|
||||||
|
<section className='text'>
|
||||||
|
{i18n('web_version')}
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</Link>
|
||||||
|
{
|
||||||
|
config?.workshopId && (
|
||||||
|
<Link
|
||||||
|
reloadDocument
|
||||||
|
to={`https://steamcommunity.com/sharedfiles/filedetails/?id=${config.workshopId}`}
|
||||||
|
target='_blank'
|
||||||
|
className='extra-links-item'
|
||||||
|
style={{
|
||||||
|
color: config?.color
|
||||||
|
}}>
|
||||||
|
<section className='content'>
|
||||||
<section className='content-text'>
|
<section className='content-text'>
|
||||||
<section className="outline" />
|
<section className="outline" />
|
||||||
<section className='text'>{option.name}</section>
|
<section className='text'>
|
||||||
<section className='tick-icon' />
|
{i18n('steam_workshop')}
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
)
|
</Link>
|
||||||
})}
|
)
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<section className='settings-title-wrapper'>
|
|
||||||
<section className='text'>
|
|
||||||
<Dropdown
|
|
||||||
text={i18n('backgrounds')}
|
|
||||||
menu={backgrounds.map((item) => {
|
|
||||||
return {
|
|
||||||
name: item,
|
|
||||||
value: item
|
|
||||||
}
|
}
|
||||||
})}
|
</section>
|
||||||
onClick={(item) => {
|
</section>
|
||||||
setCurrentBackground(item.name)
|
</section>
|
||||||
}}
|
<section className="spine-container" style={{
|
||||||
className='backgrounds-dropdown'
|
backgroundImage: `url(/${key}/assets/${import.meta.env.VITE_BACKGROUND_FOLDER}/${currentBackground})`
|
||||||
activeRule={(item) => {
|
}} >
|
||||||
return item?.name === currentBackground
|
{
|
||||||
}}
|
config && (
|
||||||
activeColor={{
|
<img src={`/${config.link}/assets/${config.logo}.png`} alt={config?.codename[language]} className='operator-logo'/>
|
||||||
color: config?.color
|
)
|
||||||
}}
|
}
|
||||||
/>
|
<section ref={spineRef} />
|
||||||
|
{
|
||||||
|
subtitle && (
|
||||||
|
<section className={`voice-wrapper${hideSubtitle ? '' : ' active'}`}>
|
||||||
|
<section className='voice-title'>{subtitle.title}</section>
|
||||||
|
<section className='voice-subtitle'>
|
||||||
|
<span>{subtitle.text}</span>
|
||||||
|
<span className='voice-triangle' />
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
<MainBorder />
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -162,9 +162,12 @@
|
|||||||
|
|
||||||
.main .main-header .main-tab {
|
.main .main-header .main-tab {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
text-align: right;
|
white-space: pre;
|
||||||
white-space: nowrap;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main .main-header .main-tab .main-tab-item {
|
.main .main-header .main-tab .main-tab-item {
|
||||||
@@ -178,11 +181,24 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main .main-header .main-tab .main-tab-item .main-tab-text-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .main-header .main-tab .main-tab-item.active .main-tab-text-wrapper,
|
||||||
|
.main .main-header .main-tab .main-tab-item:hover .main-tab-text-wrapper,
|
||||||
.main .main-header .main-tab .main-tab-item.active .text,
|
.main .main-header .main-tab .main-tab-item.active .text,
|
||||||
.main .main-header .main-tab .main-tab-item:hover .text {
|
.main .main-header .main-tab .main-tab-item:hover .text {
|
||||||
color: currentColor;
|
color: currentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .main-header .main-tab .main-tab-item.active {
|
||||||
|
border-bottom-color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main .main-header .main-tab .main-tab-item.active,
|
.main .main-header .main-tab .main-tab-item.active,
|
||||||
@@ -190,13 +206,9 @@
|
|||||||
color: var(--link-highlight-color);
|
color: var(--link-highlight-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main .main-header .main-tab .main-tab-item.active {
|
.main .main-header .main-tab .main-tab-item .main-tab-text-wrapper {
|
||||||
border-bottom-color: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main .main-header .main-tab .main-tab-item .text {
|
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main .main-icon {
|
.main .main-icon {
|
||||||
@@ -211,7 +223,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: -4rem;
|
right: -4rem;
|
||||||
bottom: -24px;
|
bottom: -24px;
|
||||||
color: #666;
|
color: var(--button-color);
|
||||||
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
useContext,
|
|
||||||
useMemo,
|
useMemo,
|
||||||
useCallback
|
useCallback
|
||||||
} from 'react'
|
} from 'react'
|
||||||
@@ -53,7 +52,10 @@ export default function Root(props) {
|
|||||||
}}
|
}}
|
||||||
style={item.style}
|
style={item.style}
|
||||||
>
|
>
|
||||||
<span className='text'>{i18n(item.key)}</span>
|
<section className='main-tab-text-wrapper'>
|
||||||
|
<span className='text'>{i18n(item.key)}</span>
|
||||||
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
@@ -147,7 +149,7 @@ export default function Root(props) {
|
|||||||
<main className='main'>
|
<main className='main'>
|
||||||
<section className='main-header'>
|
<section className='main-header'>
|
||||||
<section className='main-title'>
|
<section className='main-title'>
|
||||||
{headerIcon && (
|
{headerIcon && (
|
||||||
<section className='main-icon'>
|
<section className='main-icon'>
|
||||||
<CharIcon
|
<CharIcon
|
||||||
type={headerIcon}
|
type={headerIcon}
|
||||||
@@ -156,8 +158,8 @@ export default function Root(props) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
{title}
|
{title}
|
||||||
</section>
|
</section>
|
||||||
<section className='main-tab'>
|
<section className='main-tab'>
|
||||||
{headerTabs}
|
{headerTabs}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export function useConfig() {
|
|||||||
const [version, setVersion] = useAtom(versionAtom);
|
const [version, setVersion] = useAtom(versionAtom);
|
||||||
const [operators, setOperators] = useAtom(operatorsAtom);
|
const [operators, setOperators] = useAtom(operatorsAtom);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetcher('/_assets/directory.json').then(data => {
|
fetcher('/_assets/directory.json').then(data => {
|
||||||
setConfig(data);
|
setConfig(data);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { writeSync, copy, rmdir } from './file.js'
|
import { writeSync, copy, rmdir, readSync as readFile } from './file.js'
|
||||||
import { read } from './yaml.js';
|
import { read } from './yaml.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,17 +22,49 @@ export default function ({ backgrounds, charwordTable }) {
|
|||||||
} else {
|
} else {
|
||||||
acc[date] = [cur]
|
acc[date] = [cur]
|
||||||
}
|
}
|
||||||
cur.voiceLangs = []
|
|
||||||
const voiceInfo = Object.values(charwordTable.lookup(cur.link).operator.info.zh_CN)
|
cur.workshopId = null
|
||||||
voiceInfo.forEach((item) => {
|
try {
|
||||||
cur.voiceLangs = [...cur.voiceLangs, ...Object.keys(item)]
|
cur.workshopId = JSON.parse(readFile(path.join(__projetRoot, __config.folder.operator, cur.link, 'project.json'))).workshopid
|
||||||
})
|
} catch (e) {
|
||||||
|
console.log(`No workshop id for ${cur.link}!`, e)
|
||||||
|
}
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, {}))
|
}, {}))
|
||||||
.sort((a, b) => Date.parse(b[0].date) - Date.parse(a[0].date)),
|
.sort((a, b) => Date.parse(b[0].date) - Date.parse(a[0].date)),
|
||||||
}
|
}
|
||||||
const versionJson = __config.version
|
const versionJson = __config.version
|
||||||
|
|
||||||
|
filesToCopy.forEach((operator) => {
|
||||||
|
const voiceJson = {}
|
||||||
|
|
||||||
|
voiceJson.voiceLangs = {}
|
||||||
|
voiceJson.subtitleLangs = {}
|
||||||
|
const charwordTableObj = charwordTable.lookup(operator)
|
||||||
|
const subtitleInfo = Object.keys(charwordTableObj.operator.info)
|
||||||
|
subtitleInfo.forEach((item) => {
|
||||||
|
if (Object.keys(charwordTableObj.operator.info[item]).length > 0) {
|
||||||
|
const key = item.replace("_", "-")
|
||||||
|
voiceJson.subtitleLangs[key] = {}
|
||||||
|
for (const [id, subtitles] of Object.entries(charwordTableObj.operator.voice[item])) {
|
||||||
|
const match = id.replace(/(.+?)([A-Z]\w+)/, '$2')
|
||||||
|
if (match === id) {
|
||||||
|
voiceJson.subtitleLangs[key].default = subtitles
|
||||||
|
} else {
|
||||||
|
voiceJson.subtitleLangs[key][match] = subtitles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
voiceJson.voiceLangs[key] = {}
|
||||||
|
Object.values(charwordTableObj.operator.info[item]).forEach((item) => {
|
||||||
|
voiceJson.voiceLangs[key] = { ...voiceJson.voiceLangs[key], ...item }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
writeSync(JSON.stringify(voiceJson, null), path.join(targetFolder, `voice_${operator}.json`))
|
||||||
|
})
|
||||||
|
|
||||||
const changelogs = read(path.join(__projetRoot, 'changelogs.yaml'))
|
const changelogs = read(path.join(__projetRoot, 'changelogs.yaml'))
|
||||||
const changelogsArray = Object.keys(changelogs).reduce((acc, cur) => {
|
const changelogsArray = Object.keys(changelogs).reduce((acc, cur) => {
|
||||||
const array = []
|
const array = []
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ class ViteRunner {
|
|||||||
}, {
|
}, {
|
||||||
key: "voice_folders",
|
key: "voice_folders",
|
||||||
value: JSON.stringify(this.#globalConfig.folder.voice)
|
value: JSON.stringify(this.#globalConfig.folder.voice)
|
||||||
|
}, {
|
||||||
|
key: "directory_folder",
|
||||||
|
value: JSON.stringify(this.#globalConfig.folder.directory)
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
key: "background_folder",
|
||||||
|
value: JSON.stringify(this.#globalConfig.folder.background)
|
||||||
}
|
}
|
||||||
]), path.join(directoryDir, '.env'))
|
]), path.join(directoryDir, '.env'))
|
||||||
this.#mode = process.argv[3]
|
this.#mode = process.argv[3]
|
||||||
|
|||||||
Reference in New Issue
Block a user