feat(directory): add voice
This commit is contained in:
11
aklive2d.js
11
aklive2d.js
@@ -5,20 +5,18 @@ import { fork } from 'child_process';
|
||||
import getConfig from './libs/config.js'
|
||||
import ProjectJson from './libs/project_json.js'
|
||||
import EnvGenerator from './libs/env_generator.js'
|
||||
import { write, rmdir, copy, writeSync, copyDir, readSync } from './libs/file.js'
|
||||
import { write, rmdir, copy, writeSync, copyDir } from './libs/file.js'
|
||||
import AssetsProcessor from './libs/assets_processor.js'
|
||||
import init from './libs/initializer.js'
|
||||
import directory from './libs/directory.js'
|
||||
import { appendReadme } from './libs/append.js'
|
||||
import { increase } from './libs/version.js';
|
||||
import Background from './libs/background.js'
|
||||
import CharwordTable from './libs/charword_table.js';
|
||||
|
||||
async function main() {
|
||||
global.__projetRoot = path.dirname(fileURLToPath(import.meta.url))
|
||||
global.__config = {
|
||||
...getConfig(),
|
||||
version: readSync(path.join(__projetRoot, 'Version'))
|
||||
}
|
||||
global.__config = getConfig()
|
||||
|
||||
const op = process.argv[2]
|
||||
let OPERATOR_NAMES = process.argv.slice(3);
|
||||
@@ -39,6 +37,7 @@ async function main() {
|
||||
for (const [key, _] of Object.entries(__config.operators)) {
|
||||
OPERATOR_NAMES.push(key)
|
||||
}
|
||||
increase(__projetRoot)
|
||||
break
|
||||
case 'preview':
|
||||
assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.')
|
||||
@@ -176,7 +175,7 @@ async function main() {
|
||||
value: __config.operators[OPERATOR_NAME].link
|
||||
}, {
|
||||
key: "version",
|
||||
value: __config.version
|
||||
value: __config.version.showcase
|
||||
}, {
|
||||
key: "title",
|
||||
value: __config.operators[OPERATOR_NAME].title
|
||||
|
||||
@@ -21,6 +21,9 @@ share:
|
||||
title:
|
||||
zh-CN: "明日方舟:"
|
||||
en-US: "Arknights: "
|
||||
directory:
|
||||
title: AKLive2D
|
||||
voice: jp/CN_037.ogg
|
||||
operators:
|
||||
chen: !include config/chen.yaml
|
||||
dusk: !include config/dusk.yaml
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
VITE_APP_TITLE="AKLive2D"
|
||||
VITE_APP_TITLE=AKLive2D
|
||||
VITE_VERSION=0.5.5
|
||||
VITE_APP_VOICE_URL=jp/CN_037.ogg
|
||||
1
directory/Version
Normal file
1
directory/Version
Normal file
@@ -0,0 +1 @@
|
||||
0.5.6
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
:root {
|
||||
--text-color: rgba(255, 255, 255, 0.87);
|
||||
--text-color-full: #fff;
|
||||
--secondary-text-color: #686a72;
|
||||
--date-color: rgba(255, 255, 255, 0.2);
|
||||
--border-color: #707070;
|
||||
@@ -17,7 +18,7 @@
|
||||
|
||||
font-family: "Geometos", "Noto Sans SC", sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.1em;
|
||||
line-height: 1.2em;
|
||||
font-weight: 400;
|
||||
|
||||
color: var(--text-color);
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
user-select: none;
|
||||
z-index: 2;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown .text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown .content {
|
||||
@@ -18,6 +18,9 @@
|
||||
}
|
||||
|
||||
.dropdown .icon {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
right: -0.1rem;
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
display: inline-block;
|
||||
@@ -27,10 +30,10 @@
|
||||
border-right: 0.15em solid var(--text-color);
|
||||
border-top: 0.15em solid var(--text-color);
|
||||
transform: translate(0, -0.15em) rotate(-45deg);
|
||||
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.dropdown .icon.active {
|
||||
.dropdown.active .icon,
|
||||
.dropdown:hover .icon {
|
||||
animation: icon-flash 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
|
||||
}
|
||||
|
||||
@@ -55,7 +58,7 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.dropdown .menu.active {
|
||||
.dropdown.active .menu {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
@@ -76,7 +79,9 @@
|
||||
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.dropdown .menu .item:hover, .dropdown .menu .item:focus, .dropdown .menu .item.active {
|
||||
.dropdown .menu .item:hover,
|
||||
.dropdown .menu .item:focus,
|
||||
.dropdown .menu .item.active {
|
||||
color: var(--link-highlight-color);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
useState,
|
||||
useEffect
|
||||
useState
|
||||
} from 'react'
|
||||
import './dropdown.css'
|
||||
|
||||
@@ -13,15 +12,15 @@ export default function Dropdown(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='dropdown'>
|
||||
<section className={`dropdown ${hidden ? '' : 'active'}`} >
|
||||
<section
|
||||
className='text'
|
||||
onClick={() => toggleDropdown()}
|
||||
>
|
||||
<span className='content'>{props.text}</span>
|
||||
<span className={`icon ${hidden ? '' : 'active'}`}></span>
|
||||
<span className='icon'></span>
|
||||
</section>
|
||||
<ul className={`menu ${hidden ? '' : 'active'}`}>
|
||||
<ul className='menu'>
|
||||
{
|
||||
props.menu.map((item) => {
|
||||
return (
|
||||
|
||||
62
directory/src/component/switch.css
Normal file
62
directory/src/component/switch.css
Normal file
@@ -0,0 +1,62 @@
|
||||
.switch {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
z-index: 2;
|
||||
padding: 8px 36px 8px 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: var(--secondary-text-color);
|
||||
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.switch.active {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.switch .content {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.switch .icon-wrapper {
|
||||
color: var(--secondary-text-color);
|
||||
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.switch.active .icon-wrapper {
|
||||
color: var(--text-color-full);
|
||||
}
|
||||
|
||||
.switch .icon {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 18px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border-left: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
border-right: 2px solid currentColor;
|
||||
border-top: 2px solid currentColor;
|
||||
transform: translate(0, -2px) rotate(-45deg);
|
||||
transition: right cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s, background-color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.switch.active .icon {
|
||||
background-color: currentColor;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.switch .icon-line {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: 6px;
|
||||
width: 18px;
|
||||
height: 2px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-color: currentColor;
|
||||
z-index: -1;
|
||||
}
|
||||
23
directory/src/component/switch.jsx
Normal file
23
directory/src/component/switch.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import './switch.css';
|
||||
|
||||
export default function Switch(props) {
|
||||
const [on, setOn] = useState(props.on)
|
||||
|
||||
useEffect(() => {
|
||||
setOn(props.on)
|
||||
}, [props.on])
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`switch ${on ? 'active' : ''}`}
|
||||
onClick={() => props.handleOnClick()}
|
||||
>
|
||||
<span className='text'>{props.text}</span>
|
||||
<section className='icon-wrapper'>
|
||||
<span className='icon-line'></span>
|
||||
<span className='icon'></span>
|
||||
</section>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export function HeaderProvider(props) {
|
||||
<HeaderContext.Provider value={{
|
||||
title, setTitle,
|
||||
tabs, setTabs,
|
||||
currentTab, setCurrentTab,
|
||||
currentTab, setCurrentTab
|
||||
}}>
|
||||
{props.children}
|
||||
</HeaderContext.Provider>
|
||||
|
||||
@@ -3,57 +3,65 @@
|
||||
"zh-CN", "en-US"
|
||||
],
|
||||
"key": {
|
||||
"dynamic_compile": {
|
||||
"zh-CN": "干员动态集录",
|
||||
"en-US": "Dynamic Compile"
|
||||
},
|
||||
"home": {
|
||||
"zh-CN": "首页",
|
||||
"en-US": "Home"
|
||||
},
|
||||
"changelogs": {
|
||||
"zh-CN": "更新日志",
|
||||
"en-US": "Changelogs"
|
||||
},
|
||||
"offical_page": {
|
||||
"zh-CN": "官方页面",
|
||||
"en-US": "Offical Page"
|
||||
},
|
||||
"disclaimer": {
|
||||
"zh-CN": "免责声明",
|
||||
"en-US": "Disclaimer"
|
||||
},
|
||||
"disclaimer_content": {
|
||||
"zh-CN": "本网站由 Halyul 设立并为明日方舟社区服务,Halyul 声明本网站完全独立运营,与 上海鹰角网络科技有限公司, Esoteric Software LLC 或其任何关联实体并无任何联系。",
|
||||
"en-US": "This website is set up and operated by Halyul for the benefit of the Arknights Community. Halyul hereby states that this website is dedicated, but not related to Hypergryph Co., Ltd, Esoteric Software LLC or any of its affiliated entity."
|
||||
},
|
||||
"privacy_policy": {
|
||||
"zh-CN": "隐私政策",
|
||||
"en-US": "Privacy Policy"
|
||||
},
|
||||
"contact_us": {
|
||||
"zh-CN": "联系我们",
|
||||
"en-US": "Contact Us"
|
||||
},
|
||||
"all": {
|
||||
"zh-CN": "综合",
|
||||
"en-US": "All"
|
||||
},
|
||||
"elite2": {
|
||||
"zh-CN": "干员晋升",
|
||||
"en-US": "Elite 2"
|
||||
},
|
||||
"skin": {
|
||||
"zh-CN": "干员时装",
|
||||
"en-US": "Skin"
|
||||
},
|
||||
"zh-CN": {
|
||||
"zh-CN": "简体中文",
|
||||
"en-US": "Chinese (Simplified)"
|
||||
},
|
||||
"en-US": {
|
||||
"zh-CN": "英语",
|
||||
"en-US": "English"
|
||||
}
|
||||
"dynamic_compile": {
|
||||
"zh-CN": "动态集录",
|
||||
"en-US": "Dynamic Compile"
|
||||
},
|
||||
"home": {
|
||||
"zh-CN": "首页",
|
||||
"en-US": "Home"
|
||||
},
|
||||
"changelogs": {
|
||||
"zh-CN": "更新日志",
|
||||
"en-US": "Changelogs"
|
||||
},
|
||||
"offical_page": {
|
||||
"zh-CN": "官方页面",
|
||||
"en-US": "Offical Page"
|
||||
},
|
||||
"disclaimer": {
|
||||
"zh-CN": "免责声明",
|
||||
"en-US": "Disclaimer"
|
||||
},
|
||||
"disclaimer_content": {
|
||||
"zh-CN": "本网站由 Halyul 设立并为明日方舟社区服务,Halyul 声明本网站完全独立运营,与 上海鹰角网络科技有限公司, Esoteric Software LLC 或其任何关联实体并无任何联系。",
|
||||
"en-US": "This website is set up and operated by Halyul for the benefit of the Arknights Community. Halyul hereby states that this website is dedicated, but not related to Hypergryph Co., Ltd, Esoteric Software LLC or any of its affiliated entity."
|
||||
},
|
||||
"privacy_policy": {
|
||||
"zh-CN": "隐私政策",
|
||||
"en-US": "Privacy Policy"
|
||||
},
|
||||
"contact_us": {
|
||||
"zh-CN": "联系我们",
|
||||
"en-US": "Contact Us"
|
||||
},
|
||||
"all": {
|
||||
"zh-CN": "综合",
|
||||
"en-US": "All"
|
||||
},
|
||||
"elite2": {
|
||||
"zh-CN": "干员晋升",
|
||||
"en-US": "Elite 2"
|
||||
},
|
||||
"skin": {
|
||||
"zh-CN": "干员时装",
|
||||
"en-US": "Skin"
|
||||
},
|
||||
"voice": {
|
||||
"zh-CN": "语音",
|
||||
"en-US": "Voice"
|
||||
},
|
||||
"live2d": {
|
||||
"zh-CN": "Live2D",
|
||||
"en-US": "Live2D"
|
||||
},
|
||||
"zh-CN": {
|
||||
"zh-CN": "简体中文",
|
||||
"en-US": "Chinese (Simplified)"
|
||||
},
|
||||
"en-US": {
|
||||
"zh-CN": "英语",
|
||||
"en-US": "English"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 0.1rem;
|
||||
flex: auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.home .item-group .item {
|
||||
@@ -41,7 +42,7 @@
|
||||
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||
}
|
||||
|
||||
.home .item-group .item .item-outline{
|
||||
.home .item-group .item .item-outline {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: -6px;
|
||||
@@ -86,11 +87,17 @@
|
||||
background-color: var(--home-item-hover-background-color);
|
||||
}
|
||||
|
||||
.home .item-group .item .item-img .live2d {
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.home .item-group .item .item-info {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
padding: 0.8rem 0.4rem;
|
||||
line-height: 1;
|
||||
line-height: 1.2em;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.home .item-group .item .item-info .item-title-container {
|
||||
@@ -109,11 +116,15 @@
|
||||
}
|
||||
|
||||
.home .item-group .item .item-info .item-title-container .item-title {
|
||||
line-height: 1.1em;
|
||||
height: 1.25rem;
|
||||
line-height: 1.3em;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.home .item-group .item .item-info .item-title-container .item-type {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
text-align: center;
|
||||
width: 1.5rem;
|
||||
fill: var(--text-color)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ import {
|
||||
useState,
|
||||
useEffect,
|
||||
useContext,
|
||||
useMemo
|
||||
useRef
|
||||
} from 'react'
|
||||
import {
|
||||
Link,
|
||||
useOutletContext,
|
||||
} from "react-router-dom";
|
||||
import './home.css'
|
||||
import { ConfigContext } from '@/context/useConfigContext';
|
||||
@@ -13,14 +14,19 @@ import { LanguageContext } from '@/context/useLanguageContext';
|
||||
import { HeaderContext } from '@/context/useHeaderContext';
|
||||
import CharIcon from '@/component/char_icon';
|
||||
import MainBorder from '@/component/main_border';
|
||||
import useUmami from '@parcellab/react-use-umami'
|
||||
import useUmami from '@parcellab/react-use-umami';
|
||||
import Switch from '@/component/switch';
|
||||
import spine from '!/libs/spine-player';
|
||||
import '!/libs/spine-player.css';
|
||||
|
||||
export default function Home() {
|
||||
const _trackEvt = useUmami('/')
|
||||
const { setAppbarExtraArea } = useOutletContext()
|
||||
const {
|
||||
setTitle,
|
||||
tabs, setTabs,
|
||||
currentTab, setCurrentTab } = useContext(HeaderContext)
|
||||
setTabs,
|
||||
currentTab, setCurrentTab
|
||||
} = useContext(HeaderContext)
|
||||
const { config } = useContext(ConfigContext)
|
||||
const {
|
||||
language,
|
||||
@@ -29,6 +35,12 @@ export default function Home() {
|
||||
i18n
|
||||
} = useContext(LanguageContext)
|
||||
const [content, setContent] = useState([])
|
||||
const [voiceOn, setVoiceOn] = useState(false)
|
||||
const [live2dOn, setLive2dOn] = useState(false)
|
||||
const [audioUrl, setAudioUrl] = useState('')
|
||||
const audioEl = new Audio(audioUrl)
|
||||
const live2dRefObject = useRef({})
|
||||
const live2dSpineObject = useRef({})
|
||||
|
||||
useEffect(() => {
|
||||
setTitle('dynamic_compile')
|
||||
@@ -48,20 +60,100 @@ export default function Home() {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const value = config.reduce((acc, cur) => {
|
||||
const date = cur.date
|
||||
if (acc[date]) {
|
||||
acc[date].push(cur)
|
||||
} else {
|
||||
acc[date] = [cur]
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
setContent(Object.values(value).sort((a, b) => new Date(b[0].date) - new Date(a[0].date)))
|
||||
setContent(config?.operators || [])
|
||||
}, [config])
|
||||
|
||||
const toggleVoice = () => {
|
||||
setVoiceOn(!voiceOn)
|
||||
setAudioUrl('')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setAppbarExtraArea([
|
||||
(
|
||||
<Switch
|
||||
key="voice"
|
||||
text={i18n.key.voice[language]}
|
||||
on={voiceOn}
|
||||
handleOnClick={() => toggleVoice()}
|
||||
/>
|
||||
// ), (
|
||||
// <Switch
|
||||
// key="live2d"
|
||||
// text={i18n.key.live2d[language]}
|
||||
// on={live2dOn}
|
||||
// handleOnClick={() => setLive2dOn(!live2dOn)}
|
||||
// />
|
||||
)
|
||||
])
|
||||
}, [voiceOn, live2dOn])
|
||||
|
||||
const isShown = (type) => currentTab === 'all' || currentTab === type
|
||||
|
||||
const playVoice = (link) => {
|
||||
if (!voiceOn) return
|
||||
audioEl.src = `/${link}/assets/voice/${import.meta.env.VITE_APP_VOICE_URL}`
|
||||
let startPlayPromise = audioEl.play()
|
||||
if (startPlayPromise !== undefined) {
|
||||
startPlayPromise
|
||||
.then(() => {
|
||||
return
|
||||
})
|
||||
.catch(() => {
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getLive2d = (link) => {
|
||||
const reactEl = <section className="live2d" ref={ref => {
|
||||
live2dRefObject.current[link] = ref
|
||||
}}></section>
|
||||
return reactEl
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (live2dOn) {
|
||||
Object.keys(live2dRefObject.current).forEach((link) => {
|
||||
const ref = live2dRefObject.current[link]
|
||||
if (ref) {
|
||||
if (live2dSpineObject.current[link]) {
|
||||
return
|
||||
}
|
||||
fetch(`/_assets/dyn_portrait_char_2014_nian_nian%234.json`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
live2dSpineObject.current[link] = new spine.SpinePlayer(ref, {
|
||||
skelUrl: `./assets/dyn_portrait_char_2014_nian_nian%234.skel`,
|
||||
atlasUrl: `./assets/dyn_portrait_char_2014_nian_nian%234.atlas`,
|
||||
rawDataURIs: data,
|
||||
animation: "Idle",
|
||||
premultipliedAlpha: true,
|
||||
alpha: true,
|
||||
backgroundColor: "#00000000",
|
||||
viewport: {
|
||||
debugRender: false,
|
||||
padLeft: `0%`,
|
||||
padRight: `0%`,
|
||||
padTop: `0%`,
|
||||
padBottom: `0%`,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
showControls: false,
|
||||
touch: false,
|
||||
fps: 60,
|
||||
defaultMix: 0,
|
||||
success: function (widget) {
|
||||
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [live2dOn])
|
||||
|
||||
return (
|
||||
<section className="home">
|
||||
{
|
||||
@@ -71,31 +163,44 @@ export default function Home() {
|
||||
<section className="item-group-wrapper" key={v[0].date} hidden={length === 0}>
|
||||
<section className="item-group">
|
||||
{v.map(item => {
|
||||
return (<Link reloadDocument to={`/${item.link}`} className="item" key={item.link} hidden={!isShown(item.type)}>
|
||||
<section className="item-background-filler" />
|
||||
<section className="item-outline" />
|
||||
<section className="item-img">
|
||||
<img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
|
||||
</section>
|
||||
<section className="item-info">
|
||||
<section className="item-title-container">
|
||||
<section className="item-title">{item.codename[language]}</section>
|
||||
<section className="item-type">
|
||||
<CharIcon
|
||||
type={item.type}
|
||||
viewBox={
|
||||
item.type === 'operator' ? '0 0 88.969 71.469' : '0 0 94.563 67.437'
|
||||
} />
|
||||
return (
|
||||
<Link
|
||||
reloadDocument
|
||||
to={`/${item.link}`}
|
||||
className="item"
|
||||
key={item.link}
|
||||
hidden={!isShown(item.type)}
|
||||
onMouseEnter={(e) => playVoice(item.link)}
|
||||
>
|
||||
<section className="item-background-filler" />
|
||||
<section className="item-outline" />
|
||||
<section className="item-img">
|
||||
{live2dOn && item.portrait !== null ? (
|
||||
getLive2d(item.link)
|
||||
) : (
|
||||
<img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
|
||||
)}
|
||||
</section>
|
||||
<section className="item-info">
|
||||
<section className="item-title-container">
|
||||
<section className="item-title">{item.codename[language]}</section>
|
||||
<section className="item-type">
|
||||
<CharIcon
|
||||
type={item.type}
|
||||
viewBox={
|
||||
item.type === 'operator' ? '0 0 88.969 71.469' : '0 0 94.563 67.437'
|
||||
} />
|
||||
</section>
|
||||
</section>
|
||||
<section className="item-text-wrapper">
|
||||
<span className='item-text'>{item.codename[language.startsWith("en") ? alternateLang : textDefaultLang]}</span>
|
||||
</section>
|
||||
<section className="item-info-background" style={{
|
||||
color: item.color
|
||||
}} />
|
||||
</section>
|
||||
<section className="item-text-wrapper">
|
||||
<span className='item-text'>{item.codename[language.startsWith("en") ? alternateLang : textDefaultLang]}</span>
|
||||
</section>
|
||||
<section className="item-info-background" style={{
|
||||
color: item.color
|
||||
}} />
|
||||
</section>
|
||||
</Link>)
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
<section className='item-group-date'>{v[0].date}</section>
|
||||
</section>
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.header .spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.header .dropdown {
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -140,7 +144,7 @@
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
line-height: 1.1em;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.main .main-header .main-tab {
|
||||
@@ -174,7 +178,7 @@
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main .main-header .return-button {
|
||||
.main .return-button {
|
||||
right: -3.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,15 @@ export default function Root(props) {
|
||||
alternateLang,
|
||||
i18n
|
||||
} = useContext(LanguageContext)
|
||||
const { title, tabs, currentTab, setCurrentTab } = useContext(HeaderContext)
|
||||
const {
|
||||
title,
|
||||
tabs,
|
||||
currentTab, setCurrentTab
|
||||
} = useContext(HeaderContext)
|
||||
const [drawerDestinations, setDrawerDestinations] = useState(null)
|
||||
const currentYear = new Date().getFullYear()
|
||||
const [headerTabs, setHeaderTabs] = useState(null)
|
||||
const [appbarExtraArea, setAppbarExtraArea] = useState(null)
|
||||
|
||||
const renderHeaderTabs = (tabs) => {
|
||||
setHeaderTabs(tabs?.map((item) => {
|
||||
@@ -111,6 +116,8 @@ export default function Root(props) {
|
||||
<section className='bar'></section>
|
||||
<section className='bar'></section>
|
||||
</section>
|
||||
<section className='spacer' />
|
||||
{appbarExtraArea}
|
||||
<Dropdown
|
||||
text={i18n.key[language][language]}
|
||||
menu={i18n.available.map((item) => {
|
||||
@@ -150,7 +157,7 @@ export default function Root(props) {
|
||||
}}
|
||||
/>
|
||||
</MainBorder>
|
||||
<Outlet />
|
||||
<Outlet context={{setAppbarExtraArea}}/>
|
||||
</main>
|
||||
<footer className='footer'>
|
||||
<section className='links section'>
|
||||
@@ -176,6 +183,7 @@ export default function Root(props) {
|
||||
<span>Spine Runtimes © 2013 - 2019 Esoteric Software LLC</span>
|
||||
<span>Assets © 2017 - {currentYear} Arknights/Hypergryph Co., Ltd</span>
|
||||
<span>Source Code © 2021 - {currentYear} Halyul</span>
|
||||
<span>Version: {import.meta.env.VITE_APP_VERSION}</span>
|
||||
</section>
|
||||
</footer>
|
||||
</>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@parcellab/react-use-umami": "^2.0.1",
|
||||
"@react-hook/cache": "^1.1.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"node-fetch": "^3.3.0",
|
||||
"react": "^18.2.0",
|
||||
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -2,6 +2,7 @@ lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@parcellab/react-use-umami': ^2.0.1
|
||||
'@react-hook/cache': ^1.1.1
|
||||
'@types/react': ^18.0.28
|
||||
'@types/react-dom': ^18.0.11
|
||||
'@vitejs/plugin-react-swc': ^3.2.0
|
||||
@@ -18,6 +19,7 @@ specifiers:
|
||||
|
||||
dependencies:
|
||||
'@parcellab/react-use-umami': 2.0.1_react@18.2.0
|
||||
'@react-hook/cache': 1.1.1_react@18.2.0
|
||||
dotenv: 16.0.3
|
||||
node-fetch: 3.3.0
|
||||
react: 18.2.0
|
||||
@@ -242,6 +244,23 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-hook/cache/1.1.1_react@18.2.0:
|
||||
resolution: {integrity: sha512-FY0Bwvxsv9Gu6pUbVdNylh4jyWPtQRegQM9G6yVx3l2cYjbxzaJwEXticLfsRcciCbnk7iyZfJn6mZ4N4+q9qA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
dependencies:
|
||||
'@react-hook/latest': 1.0.3_react@18.2.0
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-hook/latest/1.0.3_react@18.2.0:
|
||||
resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@remix-run/router/1.3.2:
|
||||
resolution: {integrity: sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
@@ -4,7 +4,9 @@ import { defineConfig } from 'vite'
|
||||
import assert from 'assert'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import getConfig from './libs/config.js'
|
||||
import { rmdir } from './libs/file.js'
|
||||
import { rmdir, writeSync } from './libs/file.js'
|
||||
import { increase } from './libs/version.js'
|
||||
import EnvGenerator from './libs/env_generator.js'
|
||||
|
||||
global.__projetRoot = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
@@ -28,7 +30,10 @@ class ViteRunner {
|
||||
this.#mode = temp[0] === "vite" ? temp[1] : process.argv[2]
|
||||
switch (this.#mode) {
|
||||
case 'directory':
|
||||
result = this.#directoryConfig
|
||||
result = {
|
||||
data: this.#directoryConfig,
|
||||
versionDir: path.join(__projetRoot, "directory"),
|
||||
}
|
||||
const op = temp[2] || process.argv[3]
|
||||
if (op !== 'preview') {
|
||||
rmdir(path.resolve(__projetRoot, this.#globalConfig.folder.release, "_directory"))
|
||||
@@ -39,7 +44,10 @@ class ViteRunner {
|
||||
case 'build':
|
||||
case 'build-all':
|
||||
case 'preview':
|
||||
result = this.#operatorConfig
|
||||
result = {
|
||||
data: this.#operatorConfig,
|
||||
versionDir: path.join(__projetRoot),
|
||||
}
|
||||
break
|
||||
default:
|
||||
return
|
||||
@@ -48,12 +56,14 @@ class ViteRunner {
|
||||
}
|
||||
|
||||
start() {
|
||||
const viteConfig = this.config;
|
||||
const configObj = this.config
|
||||
const viteConfig = configObj.data;
|
||||
switch (this.#mode) {
|
||||
case 'dev':
|
||||
this.#dev(viteConfig)
|
||||
break
|
||||
case 'build':
|
||||
increase(configObj.versionDir)
|
||||
case 'build-all':
|
||||
this.#build(viteConfig)
|
||||
break
|
||||
@@ -122,7 +132,22 @@ class ViteRunner {
|
||||
}
|
||||
|
||||
get #directoryConfig() {
|
||||
if (process.env.npm_lifecycle_event === 'vite:directory:build') {
|
||||
increase(path.join(__projetRoot, "directory"))
|
||||
}
|
||||
const directoryDir = path.resolve(__projetRoot, 'directory')
|
||||
writeSync((new EnvGenerator()).generate([
|
||||
{
|
||||
key: "app_title",
|
||||
value: this.#globalConfig.directory.title
|
||||
}, {
|
||||
key: "version",
|
||||
value: this.#globalConfig.version.directory
|
||||
}, {
|
||||
key: "app_voice_url",
|
||||
value: this.#globalConfig.directory.voice
|
||||
}
|
||||
]), path.join(directoryDir, '.env'))
|
||||
this.#mode = process.argv[3]
|
||||
return {
|
||||
...this.#baseViteConfig,
|
||||
@@ -136,6 +161,7 @@ class ViteRunner {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(directoryDir, './src'),
|
||||
'!': path.resolve(__projetRoot, './src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
@@ -151,7 +177,6 @@ class ViteRunner {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -162,4 +187,4 @@ async function main() {
|
||||
|
||||
main()
|
||||
|
||||
export default defineConfig((new ViteRunner()).config)
|
||||
export default defineConfig((new ViteRunner()).config.data)
|
||||
Reference in New Issue
Block a user