style(formatting): apply Prettier code formatting

This commit is contained in:
神奇のDz 2023-08-23 13:38:17 +08:00
parent 8d23deeb40
commit 981320feb2
28 changed files with 451 additions and 592 deletions

View File

@ -1,12 +1,11 @@
<!DOCTYPE html>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="%VITE_SITE_LOGO%">
<link rel="icon" href="%VITE_SITE_LOGO%" />
<link rel="apple-touch-icon" href="%VITE_SITE_APPLE_LOGO%" />
<link rel="bookmark" href="%VITE_SITE_APPLE_LOGO%" />
<link rel="apple-touch-icon-precomposed" sizes="200x200" href="%VITE_SITE_APPLE_LOGO%" />
@ -23,7 +22,8 @@
<script>
if (/*@cc_on!@*/ false || (!!window.MSInputMethodContext && !!document.documentMode))
window.location.href =
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" + encodeURIComponent(window.location.href)
'https://support.dmeng.net/upgrade-your-browser.html?referrer=' +
encodeURIComponent(window.location.href);
</script>
</head>
@ -36,5 +36,4 @@
</noscript>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@ -12,20 +12,12 @@
<MainRight v-show="!store.boxOpenState" />
<Box v-show="store.boxOpenState" />
</section>
<section
class="more"
v-show="store.setOpenState"
@click="store.setOpenState = false"
>
<section class="more" v-show="store.setOpenState" @click="store.setOpenState = false">
<MoreSet />
</section>
</div>
<!-- 移动端菜单按钮 -->
<Icon
class="menu"
size="24"
@click="store.mobileOpenState = !store.mobileOpenState"
>
<Icon class="menu" size="24" @click="store.mobileOpenState = !store.mobileOpenState">
<component :is="store.mobileOpenState ? CloseSmall : HamburgerButton" />
</Icon>
<!-- 页脚 -->
@ -36,19 +28,19 @@
</Transition>
</template>
<script setup>
import { helloInit, checkDays } from "@/utils/getTime.js";
import { HamburgerButton, CloseSmall } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import { Icon } from "@vicons/utils";
import Loading from "@/components/Loading.vue";
import MainLeft from "@/views/Main/Left.vue";
import MainRight from "@/views/Main/Right.vue";
import Background from "@/components/Background.vue";
import Footer from "@/components/Footer.vue";
import Box from "@/views/Box/index.vue";
import MoreSet from "@/views/MoreSet/index.vue";
import cursorInit from "@/utils/cursor.js";
import config from "@/../package.json";
import { helloInit, checkDays } from '@/utils/getTime.js';
import { HamburgerButton, CloseSmall } from '@icon-park/vue-next';
import { mainStore } from '@/store';
import { Icon } from '@vicons/utils';
import Loading from '@/components/Loading.vue';
import MainLeft from '@/views/Main/Left.vue';
import MainRight from '@/views/Main/Right.vue';
import Background from '@/components/Background.vue';
import Footer from '@/components/Footer.vue';
import Box from '@/views/Box/index.vue';
import MoreSet from '@/views/MoreSet/index.vue';
import cursorInit from '@/utils/cursor.js';
import config from '@/../package.json';
const store = mainStore();
@ -74,7 +66,7 @@ watch(
if (value < 990) {
store.boxOpenState = false;
}
}
},
);
onMounted(() => {
@ -84,7 +76,7 @@ onMounted(() => {
//
document.oncontextmenu = () => {
ElMessage({
message: "为了浏览体验,本站禁用右键",
message: '为了浏览体验,本站禁用右键',
grouping: true,
duration: 2000,
});
@ -92,11 +84,11 @@ onMounted(() => {
};
//
window.addEventListener("mousedown", (event) => {
window.addEventListener('mousedown', (event) => {
if (event.button == 1) {
store.backgroundShow = !store.backgroundShow;
ElMessage({
message: `${store.backgroundShow ? "开启" : "退出"}壁纸展示状态`,
message: `${store.backgroundShow ? '开启' : '退出'}壁纸展示状态`,
grouping: true,
});
}
@ -104,14 +96,13 @@ onMounted(() => {
//
getWidth();
window.addEventListener("resize", getWidth);
window.addEventListener('resize', getWidth);
//
const styleTitle1 =
"font-size: 20px;font-weight: 600;color: rgb(244,167,89);";
const styleTitle2 = "font-size:12px;color: rgb(244,167,89);";
const styleContent = "color: rgb(30,152,255);";
const title1 = "無名の主页";
const styleTitle1 = 'font-size: 20px;font-weight: 600;color: rgb(244,167,89);';
const styleTitle2 = 'font-size:12px;color: rgb(244,167,89);';
const styleContent = 'color: rgb(30,152,255);';
const title1 = '無名の主页';
const title2 = `
_____ __ __ _______ ____ __
|_ _| \\/ |/ ____\\ \\ / /\\ \\ / /
@ -120,16 +111,11 @@ onMounted(() => {
_| |_| | | |____) | | | | |
|_____|_| |_|_____/ |_| |_|`;
const content = `\n\n版本: ${config.version}\n主页: ${config.home}\nGithub: ${config.github}`;
console.info(
`%c${title1} %c${title2} %c${content}`,
styleTitle1,
styleTitle2,
styleContent
);
console.info(`%c${title1} %c${title2} %c${content}`, styleTitle1, styleTitle2, styleContent);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", getWidth);
window.removeEventListener('resize', getWidth);
});
</script>
@ -142,8 +128,7 @@ onBeforeUnmount(() => {
height: 100%;
transform: scale(1.2);
transition: transform 0.3s;
animation: fade-blur-main-in 0.65s cubic-bezier(0.25, 0.46, 0.45, 0.94)
forwards;
animation: fade-blur-main-in 0.65s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.5s;
.container {
width: 100%;

View File

@ -1,5 +1,5 @@
// import axios from "axios";
import fetchJsonp from "fetch-jsonp";
import fetchJsonp from 'fetch-jsonp';
/**
* 音乐播放器
@ -8,19 +8,17 @@ import fetchJsonp from "fetch-jsonp";
// 获取音乐播放列表
export const getPlayerList = async (server, type, id) => {
const res = await fetch(
`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`
`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`,
);
const data = await res.json();
if (data[0].url.startsWith("@")) {
const [handle, jsonpCallback, jsonpCallbackFunction, url] = data[0].url
.split("@")
.slice(1);
if (data[0].url.startsWith('@')) {
const [handle, jsonpCallback, jsonpCallbackFunction, url] = data[0].url.split('@').slice(1);
const jsonpData = await fetchJsonp(url).then((res) => res.json());
const domain = (
jsonpData.req_0.data.sip.find((i) => !i.startsWith("http://ws")) ||
jsonpData.req_0.data.sip.find((i) => !i.startsWith('http://ws')) ||
jsonpData.req_0.data.sip[0]
).replace("http://", "https://");
).replace('http://', 'https://');
return data.map((v, i) => ({
title: v.name || v.title,
@ -46,7 +44,7 @@ export const getPlayerList = async (server, type, id) => {
// 获取一言数据
export const getHitokoto = async () => {
const res = await fetch("https://v1.hitokoto.cn");
const res = await fetch('https://v1.hitokoto.cn');
return await res.json();
};
@ -63,7 +61,7 @@ export const getAdcode = async (key) => {
// 获取高德地理天气信息
export const getWeather = async (key, city) => {
const res = await fetch(
`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`
`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`,
);
return await res.json();
};
@ -71,6 +69,6 @@ export const getWeather = async (key, city) => {
// 获取教书先生天气 API
// https://api.oioweb.cn/doc/weather/GetWeather
export const getOtherWeather = async () => {
const res = await fetch("https://api.oioweb.cn/api/weather/GetWeather");
const res = await fetch('https://api.oioweb.cn/api/weather/GetWeather');
return await res.json();
};

View File

@ -1,31 +1,38 @@
[{
[
{
"name": "Github",
"icon": "/images/icon/github.png",
"tip": "去 Github 看看",
"url": "https://github.com/imsyy"
}, {
},
{
"name": "BiliBili",
"icon": "/images/icon/bilibili.png",
"tip": "(゜-゜)つロ 干杯 ~",
"url": "https://space.bilibili.com/98544142"
}, {
},
{
"name": "QQ",
"icon": "/images/icon/qq.png",
"tip": "有什么事吗",
"url": "https://res.abeim.cn/api/qq/?qq=1539250352"
}, {
},
{
"name": "Email",
"icon": "/images/icon/email.png",
"tip": "来封 Email ~",
"url": "mailto:one@imsyy.top"
}, {
},
{
"name": "Twitter",
"icon": "/images/icon/twitter.png",
"tip": "你懂的 ~",
"url": "https://twitter.com/iimmsyy"
}, {
},
{
"name": "Telegram",
"icon": "/images/icon/telegram.png",
"tip": "你懂的 ~",
"url": "https://t.me/bottom_user"
}]
}
]

View File

@ -24,13 +24,13 @@
</template>
<script setup>
import { mainStore } from "@/store";
import { Error } from "@icon-park/vue-next";
import { mainStore } from '@/store';
import { Error } from '@icon-park/vue-next';
const store = mainStore();
const bgUrl = ref(null);
const imgTimeout = ref(null);
const emit = defineEmits(["loadComplete"]);
const emit = defineEmits(['loadComplete']);
//
// Math.random()
@ -41,36 +41,39 @@ const changeBg = (type) => {
if (type == 0) {
bgUrl.value = `/images/background${bgRandom}.jpg`;
} else if (type == 1) {
bgUrl.value = "https://api.dujin.org/bing/1920.php";
bgUrl.value = 'https://api.dujin.org/bing/1920.php';
} else if (type == 2) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=fengjing&format=images";
bgUrl.value = 'https://api.btstu.cn/sjbz/api.php?lx=fengjing&format=images';
} else if (type == 3) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=dongman&format=images";
bgUrl.value = 'https://api.btstu.cn/sjbz/api.php?lx=dongman&format=images';
}
};
//
const imgLoadComplete = () => {
imgTimeout.value = setTimeout(() => {
imgTimeout.value = setTimeout(
() => {
store.setImgLoadStatus(true);
}, Math.floor(Math.random() * (600 - 300 + 1)) + 300);
},
Math.floor(Math.random() * (600 - 300 + 1)) + 300,
);
};
//
const imgAnimationEnd = () => {
console.log("壁纸加载且动画完成");
console.log('壁纸加载且动画完成');
//
emit("loadComplete");
emit('loadComplete');
};
//
const imgLoadError = () => {
console.error("壁纸加载失败:", bgUrl.value);
console.error('壁纸加载失败:', bgUrl.value);
ElMessage({
message: "壁纸加载失败,已临时切换回默认",
message: '壁纸加载失败,已临时切换回默认',
icon: h(Error, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
bgUrl.value = `/images/background${bgRandom}.jpg`;
@ -105,7 +108,9 @@ onBeforeUnmount(() => {
object-fit: cover;
backface-visibility: hidden;
filter: blur(20px) brightness(0.3);
transition: filter 0.3s, transform 0.3s;
transition:
filter 0.3s,
transform 0.3s;
animation: fade-blur-in 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.45s;
}
@ -116,10 +121,7 @@ onBeforeUnmount(() => {
top: 0;
width: 100%;
height: 100%;
background-image: radial-gradient(
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.5) 100%
),
background-image: radial-gradient(rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.5) 100%),
radial-gradient(rgba(0, 0, 0, 0) 33%, rgba(0, 0, 0, 0.3) 166%);
transition: 1.5s;

View File

@ -38,9 +38,9 @@
</template>
<script setup>
import { MusicOne } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import config from "@/../package.json";
import { MusicOne } from '@icon-park/vue-next';
import { mainStore } from '@/store';
import config from '@/../package.json';
const store = mainStore();
const fullYear = new Date().getFullYear();

View File

@ -26,10 +26,10 @@
</template>
<script setup>
import { MusicMenu, Error } from "@icon-park/vue-next";
import { getHitokoto } from "@/api";
import { mainStore } from "@/store";
import debounce from "@/utils/debounce.js";
import { MusicMenu, Error } from '@icon-park/vue-next';
import { getHitokoto } from '@/api';
import { mainStore } from '@/store';
import debounce from '@/utils/debounce.js';
const store = mainStore();
@ -38,8 +38,8 @@ const openMusicShow = ref(false);
//
const hitokotoData = reactive({
text: "这里应该显示一句话",
from: "無名",
text: '这里应该显示一句话',
from: '無名',
});
//
@ -51,10 +51,10 @@ const getHitokotoData = () => {
})
.catch(() => {
ElMessage({
message: "一言获取失败",
message: '一言获取失败',
icon: h(Error, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
});
@ -62,8 +62,8 @@ const getHitokotoData = () => {
//
const updateHitokoto = () => {
hitokotoData.text = "新的一言正在赶来的路上";
hitokotoData.from = "来源加载中";
hitokotoData.text = '新的一言正在赶来的路上';
hitokotoData.from = '来源加载中';
//
debounce(() => {
getHitokotoData();

View File

@ -41,24 +41,15 @@
</template>
<script setup>
import { Icon } from "@vicons/utils";
import { Icon } from '@vicons/utils';
// https://www.xicons.org
import {
Link,
Blog,
CompactDisc,
Cloud,
Compass,
Book,
Fire,
LaptopCode,
} from "@vicons/fa"; // 使
import { mainStore } from "@/store";
import { Swiper, SwiperSlide } from "swiper/vue";
import { Pagination, Mousewheel } from "swiper";
import siteLinks from "@/assets/siteLinks.json";
import "swiper/scss";
import "swiper/scss/pagination";
import { Link, Blog, CompactDisc, Cloud, Compass, Book, Fire, LaptopCode } from '@vicons/fa'; // 使
import { mainStore } from '@/store';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Pagination, Mousewheel } from 'swiper';
import siteLinks from '@/assets/siteLinks.json';
import 'swiper/scss';
import 'swiper/scss/pagination';
const store = mainStore();
@ -85,10 +76,10 @@ const siteIcon = {
//
const jumpLink = (data) => {
if (data.name === "音乐" && store.musicClick) {
if (typeof $openList === "function") $openList();
if (data.name === '音乐' && store.musicClick) {
if (typeof $openList === 'function') $openList();
} else {
window.open(data.link, "_blank");
window.open(data.link, '_blank');
}
};

View File

@ -15,7 +15,7 @@
</template>
<script setup>
import { mainStore } from "@/store";
import { mainStore } from '@/store';
const store = mainStore();
@ -52,7 +52,7 @@ const siteName = import.meta.env.VITE_SITE_NAME;
z-index: 2;
&:before {
content: "";
content: '';
position: absolute;
top: 5px;
left: 5px;
@ -65,7 +65,7 @@ const siteName = import.meta.env.VITE_SITE_NAME;
}
&:after {
content: "";
content: '';
position: absolute;
top: 15px;
left: 15px;
@ -109,7 +109,9 @@ const siteName = import.meta.env.VITE_SITE_NAME;
&.loaded {
visibility: hidden;
transform: translateY(-100%);
transition: transform 0.3s 1s ease-out, visibility 0.3s 1s ease-out;
transition:
transform 0.3s 1s ease-out,
visibility 0.3s 1s ease-out;
.loader {
.loader-circle,
.loader-text {

View File

@ -28,16 +28,16 @@
</template>
<script setup>
import { Icon } from "@vicons/utils";
import { QuoteLeft, QuoteRight } from "@vicons/fa";
import { Error } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import { Icon } from '@vicons/utils';
import { QuoteLeft, QuoteRight } from '@vicons/fa';
import { Error } from '@icon-park/vue-next';
import { mainStore } from '@/store';
const store = mainStore();
// logo
const siteLogo = import.meta.env.VITE_SITE_MAIN_LOGO;
//
const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
const siteUrl = import.meta.env.VITE_SITE_URL.split('.');
//
const descriptionText = reactive({
@ -51,11 +51,11 @@ const changeBox = () => {
store.boxOpenState = !store.boxOpenState;
} else {
ElMessage({
message: "当前页面宽度不足以开启盒子",
message: '当前页面宽度不足以开启盒子',
grouping: true,
icon: h(Error, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
}
@ -72,7 +72,7 @@ watch(
descriptionText.hello = import.meta.env.VITE_DESC_HELLO;
descriptionText.text = import.meta.env.VITE_DESC_TEXT;
}
}
},
);
</script>
@ -92,7 +92,7 @@ watch(
height: 142px;
margin-left: 12px;
transform: translateY(-8px);
font-family: "Pacifico-Regular";
font-family: 'Pacifico-Regular';
.bg {
font-size: 5rem;
@ -136,7 +136,7 @@ watch(
p {
&:nth-of-type(1) {
font-family: "Pacifico-Regular";
font-family: 'Pacifico-Regular';
}
}
}

View File

@ -11,49 +11,24 @@
<span @click="store.musicOpenState = false">回到一言</span>
</div>
<div class="control">
<go-start
theme="filled"
size="30"
fill="#efefef"
@click="changeMusicIndex(0)"
/>
<go-start theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(0)" />
<div class="state" @click="changePlayState">
<play-one
theme="filled"
size="50"
fill="#efefef"
v-show="!store.playerState"
/>
<pause
theme="filled"
size="50"
fill="#efefef"
v-show="store.playerState"
/>
<play-one theme="filled" size="50" fill="#efefef" v-show="!store.playerState" />
<pause theme="filled" size="50" fill="#efefef" v-show="store.playerState" />
</div>
<go-end
theme="filled"
size="30"
fill="#efefef"
@click="changeMusicIndex(1)"
/>
<go-end theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(1)" />
</div>
<div class="menu">
<div class="name" v-show="!volumeShow">
<span>{{
store.getPlayerData.name
? store.getPlayerData.name + " - " + store.getPlayerData.artist
: "未播放音乐"
? store.getPlayerData.name + ' - ' + store.getPlayerData.artist
: '未播放音乐'
}}</span>
</div>
<div class="volume" v-show="volumeShow">
<div class="icon">
<volume-mute
theme="filled"
size="24"
fill="#efefef"
v-if="volumeNum == 0"
/>
<volume-mute theme="filled" size="24" fill="#efefef" v-if="volumeNum == 0" />
<volume-small
theme="filled"
size="24"
@ -62,23 +37,13 @@
/>
<volume-notice theme="filled" size="24" fill="#efefef" v-else />
</div>
<el-slider
v-model="volumeNum"
:show-tooltip="false"
:min="0"
:max="1"
:step="0.01"
/>
<el-slider v-model="volumeNum" :show-tooltip="false" :min="0" :max="1" :step="0.01" />
</div>
</div>
</div>
<!-- 音乐列表弹窗 -->
<Transition name="fade">
<div
class="music-list"
v-show="musicListShow"
@click="musicListShow = false"
>
<div class="music-list" v-show="musicListShow" @click="musicListShow = false">
<Transition name="zoom">
<div class="list" v-show="musicListShow" @click.stop>
<close-one
@ -112,9 +77,9 @@ import {
VolumeMute,
VolumeSmall,
VolumeNotice,
} from "@icon-park/vue-next";
import Player from "@/components/Player.vue";
import { mainStore } from "@/store";
} from '@icon-park/vue-next';
import Player from '@/components/Player.vue';
import { mainStore } from '@/store';
const store = mainStore();
//
@ -147,8 +112,8 @@ const changeMusicIndex = (type) => {
onMounted(() => {
//
window.addEventListener("keydown", (e) => {
if (e.code == "Space") {
window.addEventListener('keydown', (e) => {
if (e.code == 'Space') {
changePlayState();
}
});
@ -162,7 +127,7 @@ watch(
(value) => {
store.musicVolume = value;
playerRef.value.changeVolume(store.musicVolume);
}
},
);
</script>

View File

@ -20,10 +20,10 @@
</template>
<script setup>
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
import { getPlayerList } from "@/api";
import { mainStore } from "@/store";
import aplayer from "vue3-aplayer";
import { MusicOne, PlayWrong } from '@icon-park/vue-next';
import { getPlayerList } from '@/api';
import { mainStore } from '@/store';
import aplayer from 'vue3-aplayer';
const store = mainStore();
@ -47,12 +47,12 @@ const props = defineProps({
//
theme: {
type: String,
default: "#efefef",
default: '#efefef',
},
//
repeat: {
type: String,
default: "list", //'list' | 'music' | 'none'
default: 'list', //'list' | 'music' | 'none'
},
//
shuffle: {
@ -70,17 +70,17 @@ const props = defineProps({
// ( netease-, tencent-qq )
songServer: {
type: String,
default: "netease", //'netease' | 'tencent'
default: 'netease', //'netease' | 'tencent'
},
// ( song-, playlist-, album-, search-, artist- )
songType: {
type: String,
default: "playlist",
default: 'playlist',
},
// id
songId: {
type: String,
default: "7452421335",
default: '7452421335',
},
//
listFolded: {
@ -90,7 +90,7 @@ const props = defineProps({
//
listMaxHeight: {
type: String,
default: "420px",
default: '420px',
},
});
@ -98,8 +98,7 @@ const props = defineProps({
onMounted(() => {
nextTick(() => {
try {
getPlayerList(props.songServer, props.songType, props.songId).then(
(res) => {
getPlayerList(props.songServer, props.songType, props.songId).then((res) => {
console.log(res);
//
playIndex.value = Math.floor(Math.random() * res.length);
@ -117,23 +116,22 @@ onMounted(() => {
});
});
console.log(
"音乐加载完成",
'音乐加载完成',
playList.value,
playIndex.value,
playListCount.value,
props.volume
);
}
props.volume,
);
});
} catch (err) {
console.error(err);
store.musicIsOk = false;
ElMessage({
message: "播放器加载失败",
message: '播放器加载失败',
grouping: true,
icon: h(PlayWrong, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
}
@ -142,20 +140,17 @@ onMounted(() => {
//
const onPlay = () => {
console.log("播放");
console.log('播放');
//
store.setPlayerState(player.value.audio.paused);
//
store.setPlayerData(
player.value.currentMusic.title,
player.value.currentMusic.artist
);
store.setPlayerData(player.value.currentMusic.title, player.value.currentMusic.artist);
ElMessage({
message: store.getPlayerData.name + " - " + store.getPlayerData.artist,
message: store.getPlayerData.name + ' - ' + store.getPlayerData.artist,
grouping: true,
icon: h(MusicOne, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
};
@ -169,12 +164,10 @@ const onPause = () => {
const onTimeUp = () => {
let playerRef = player.value.$.vnode.el;
if (playerRef) {
const currentLrcElement = playerRef.querySelector(".aplayer-lrc-current");
const currentLrcElement = playerRef.querySelector('.aplayer-lrc-current');
const previousLrcElement = currentLrcElement?.previousElementSibling;
const lrcContent =
currentLrcElement?.innerHTML ||
previousLrcElement?.innerHTML ||
"这句没有歌词";
currentLrcElement?.innerHTML || previousLrcElement?.innerHTML || '这句没有歌词';
store.setPlayerLrc(lrcContent);
}
};
@ -218,7 +211,7 @@ defineExpose({ playToggle, changeVolume, changeSong });
width: 80%;
background: transparent;
border-radius: 6px;
font-family: "HarmonyOS_Regular", sans-serif !important;
font-family: 'HarmonyOS_Regular', sans-serif !important;
:deep(.aplayer-body) {
.aplayer-pic {
display: none;

View File

@ -3,11 +3,7 @@
<el-collapse class="collapse" v-model="activeName" accordion>
<el-collapse-item title="个性壁纸" name="1">
<div class="bg-set">
<el-radio-group
v-model="coverType"
text-color="#ffffff"
@change="radioChange"
>
<el-radio-group v-model="coverType" text-color="#ffffff" @change="radioChange">
<el-radio label="0" size="large" border>默认壁纸</el-radio>
<el-radio label="1" size="large" border>每日一图</el-radio>
<el-radio label="2" size="large" border>随机风景</el-radio>
@ -64,24 +60,23 @@
</template>
<script setup>
import { CheckSmall, CloseSmall, SuccessPicture } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import { storeToRefs } from "pinia";
import { CheckSmall, CloseSmall, SuccessPicture } from '@icon-park/vue-next';
import { mainStore } from '@/store';
import { storeToRefs } from 'pinia';
const store = mainStore();
const { coverType, siteStartShow, musicClick, playerLrcShow, footerBlur } =
storeToRefs(store);
const { coverType, siteStartShow, musicClick, playerLrcShow, footerBlur } = storeToRefs(store);
//
const activeName = ref("1");
const activeName = ref('1');
//
const radioChange = () => {
ElMessage({
message: "壁纸设置成功,刷新后生效",
message: '壁纸设置成功,刷新后生效',
icon: h(SuccessPicture, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
};

View File

@ -18,10 +18,10 @@
</template>
<script setup>
import socialLinks from "@/assets/socialLinks.json";
import socialLinks from '@/assets/socialLinks.json';
//
const socialTip = ref("通过这里联系我吧");
const socialTip = ref('通过这里联系我吧');
</script>
<style lang="scss" scoped>
@ -37,7 +37,9 @@ const socialTip = ref("通过这里联系我吧");
border-radius: 6px;
backdrop-filter: blur(0);
animation: fade 0.5s;
transition: background-color 0.3s, backdrop-filter 0.3s;
transition:
background-color 0.3s,
backdrop-filter 0.3s;
@media (max-width: 840px) {
max-width: 100%;
justify-content: center;

View File

@ -1,45 +1,17 @@
<template>
<div class="time-capsule">
<div class="title">
<hourglass-full
theme="two-tone"
size="24"
:fill="['#efefef', '#00000020']"
/>
<hourglass-full theme="two-tone" size="24" :fill="['#efefef', '#00000020']" />
<span>时光胶囊</span>
</div>
<span class="text"
>今日已经度过了&nbsp;{{ timeData.day.elapsed }}&nbsp;小时</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.day.pass"
/>
<span class="text"
>本周已经度过了&nbsp;{{ timeData.week.elapsed }}&nbsp;</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.week.pass"
/>
<span class="text"
>本月已经度过了&nbsp;{{ timeData.month.elapsed }}&nbsp;</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.month.pass"
/>
<span class="text"
>今年已经度过了&nbsp;{{ timeData.year.elapsed }}&nbsp;个月</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.year.pass"
/>
<span class="text">今日已经度过了&nbsp;{{ timeData.day.elapsed }}&nbsp;小时</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.day.pass" />
<span class="text">本周已经度过了&nbsp;{{ timeData.week.elapsed }}&nbsp;</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.week.pass" />
<span class="text">本月已经度过了&nbsp;{{ timeData.month.elapsed }}&nbsp;</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.month.pass" />
<span class="text">今年已经度过了&nbsp;{{ timeData.year.elapsed }}&nbsp;个月</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.year.pass" />
<div v-if="startDate?.length >= 4 && store.siteStartShow">
<span class="text" v-html="startDateText" />
<!-- <el-progress
@ -54,9 +26,9 @@
</template>
<script setup>
import { HourglassFull } from "@icon-park/vue-next";
import { getTimeCapsule, siteDateStatistics } from "@/utils/getTime.js";
import { mainStore } from "@/store";
import { HourglassFull } from '@icon-park/vue-next';
import { getTimeCapsule, siteDateStatistics } from '@/utils/getTime.js';
import { mainStore } from '@/store';
const store = mainStore();
//
@ -68,8 +40,7 @@ const timeInterval = ref(null);
onMounted(() => {
timeInterval.value = setInterval(() => {
timeData.value = getTimeCapsule();
if (startDate.value)
startDateText.value = siteDateStatistics(new Date(startDate.value));
if (startDate.value) startDateText.value = siteDateStatistics(new Date(startDate.value));
}, 1000);
});

View File

@ -5,9 +5,9 @@
<span>{{ weatherData.weather.temperature }}</span>
<span class="sm-hidden">
&nbsp;{{
weatherData.weather.winddirection?.endsWith("风")
weatherData.weather.winddirection?.endsWith('风')
? weatherData.weather.winddirection
: weatherData.weather.winddirection + "风"
: weatherData.weather.winddirection + '风'
}}&nbsp;
</span>
<span class="sm-hidden">{{ weatherData.weather.windpower }}&nbsp;</span>
@ -18,8 +18,8 @@
</template>
<script setup>
import { getAdcode, getWeather, getOtherWeather } from "@/api";
import { Error } from "@icon-park/vue-next";
import { getAdcode, getWeather, getOtherWeather } from '@/api';
import { Error } from '@icon-park/vue-next';
// Key
const mainKey = import.meta.env.VITE_WEATHER_KEY;
@ -58,8 +58,8 @@ const getWeatherData = () => {
};
})
.catch((err) => {
console.error("天气信息获取失败:" + err);
onError("天气信息获取失败");
console.error('天气信息获取失败:' + err);
onError('天气信息获取失败');
});
} else {
getAdcode(mainKey)
@ -79,13 +79,13 @@ const getWeatherData = () => {
};
})
.catch((err) => {
console.error("天气信息获取失败:" + err);
onError("天气信息获取失败");
console.error('天气信息获取失败:' + err);
onError('天气信息获取失败');
});
})
.catch((err) => {
console.error("地理位置获取失败:" + err);
onError("地理位置获取失败");
console.error('地理位置获取失败:' + err);
onError('地理位置获取失败');
});
}
};
@ -95,8 +95,8 @@ const onError = (message) => {
ElMessage({
message,
icon: h(Error, {
theme: "filled",
fill: "#efefef",
theme: 'filled',
fill: '#efefef',
}),
});
console.error(message);

View File

@ -1,12 +1,8 @@
import {
createApp
} from 'vue';
import { createApp } from 'vue';
import '@/style/style.scss';
import App from '@/App.vue';
// 引入 pinia
import {
createPinia
} from 'pinia';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const app = createApp(App);
@ -14,11 +10,11 @@ const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.mount('#app')
app.mount('#app');
// PWA
navigator.serviceWorker.addEventListener('controllerchange', () => {
// 弹出更新提醒
console.log("站点已更新,刷新后生效");
ElMessage("站点已更新,刷新后生效");
})
console.log('站点已更新,刷新后生效');
ElMessage('站点已更新,刷新后生效');
});

View File

@ -1,11 +1,11 @@
import { defineStore } from "pinia";
import { defineStore } from 'pinia';
export const mainStore = defineStore("main", {
export const mainStore = defineStore('main', {
state: () => {
return {
imgLoadStatus: false, // 壁纸加载状态
innerWidth: null, // 当前窗口宽度
coverType: "0", // 壁纸种类
coverType: '0', // 壁纸种类
siteStartShow: false, // 建站日期显示
musicClick: false, // 音乐链接是否跳转
musicIsOk: false, // 音乐是否加载完成
@ -19,7 +19,7 @@ export const mainStore = defineStore("main", {
playerState: false, // 当前播放状态
playerTitle: null, // 当前播放歌曲名
playerArtist: null, // 当前播放歌手名
playerLrc: "歌词加载中", // 当前播放歌词
playerLrc: '歌词加载中', // 当前播放歌词
playerLrcShow: true, // 是否显示底栏歌词
footerBlur: true, // 底栏模糊
};
@ -73,15 +73,15 @@ export const mainStore = defineStore("main", {
},
},
persist: {
key: "data",
key: 'data',
storage: window.localStorage,
paths: [
"coverType",
"musicVolume",
"siteStartShow",
"musicClick",
"playerLrcShow",
"footerBlur",
'coverType',
'musicVolume',
'siteStartShow',
'musicClick',
'playerLrcShow',
'footerBlur',
],
},
});

View File

@ -29,7 +29,6 @@
/* 小于720px时 */
@media (max-width: 720px) {
// 隐藏元素
.xs-hidden {
display: none;

View File

@ -17,7 +17,7 @@ body {
padding: 0;
background-color: #333;
overflow: hidden;
font-family: "HarmonyOS_Regular", sans-serif;
font-family: 'HarmonyOS_Regular', sans-serif;
}
*,
@ -37,13 +37,13 @@ p {
// 字体文件
@font-face {
font-family: "Pacifico-Regular";
src: url("/font/Pacifico-Regular.ttf") format("truetype");
font-family: 'Pacifico-Regular';
src: url('/font/Pacifico-Regular.ttf') format('truetype');
}
@font-face {
font-family: "UnidreamLED";
src: url("/font/UnidreamLED.ttf") format("truetype");
font-family: 'UnidreamLED';
src: url('/font/UnidreamLED.ttf') format('truetype');
}
// 基础样式
@ -62,7 +62,9 @@ p {
background-color: #00000040;
backdrop-filter: blur(10px);
transform: scale(1);
transition: backdrop-filter 0.3s, transform 0.3s;
transition:
backdrop-filter 0.3s,
transform 0.3s;
&:hover {
transform: scale(1.01);
}
@ -104,7 +106,7 @@ p {
background-color: #efefef;
border-radius: 6px;
text-align: center;
font-family: "UnidreamLED";
font-family: 'UnidreamLED';
span {
color: #564d59;
font-size: 0.9rem;

View File

@ -4,18 +4,16 @@ Math.lerp = (a, b, n) => (1 - n) * a + n * b;
const getStyle = (el, attr) => {
try {
return window.getComputedStyle ?
window.getComputedStyle(el)[attr] :
el.currentStyle[attr];
return window.getComputedStyle ? window.getComputedStyle(el)[attr] : el.currentStyle[attr];
} catch (e) {}
return "";
return '';
};
class Cursor {
constructor() {
this.pos = {
curr: null,
prev: null
prev: null,
};
this.pt = [];
this.create();
@ -24,34 +22,33 @@ class Cursor {
}
move(left, top) {
this.cursor.style["left"] = `${left}px`;
this.cursor.style["top"] = `${top}px`;
this.cursor.style['left'] = `${left}px`;
this.cursor.style['top'] = `${top}px`;
}
create() {
if (!this.cursor) {
this.cursor = document.createElement("div");
this.cursor.id = "cursor";
this.cursor.classList.add("xs-hidden");
this.cursor.classList.add("hidden");
this.cursor = document.createElement('div');
this.cursor.id = 'cursor';
this.cursor.classList.add('xs-hidden');
this.cursor.classList.add('hidden');
document.body.append(this.cursor);
}
var el = document.getElementsByTagName('*');
for (let i = 0; i < el.length; i++)
if (getStyle(el[i], "cursor") == "pointer")
this.pt.push(el[i].outerHTML);
if (getStyle(el[i], 'cursor') == 'pointer') this.pt.push(el[i].outerHTML);
document.body.appendChild((this.scr = document.createElement("style")));
document.body.appendChild((this.scr = document.createElement('style')));
this.scr.innerHTML = `* {cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='10px' height='10px'><circle cx='4' cy='4' r='4' fill='white' /></svg>") 4 4, auto !important}`;
}
refresh() {
this.scr.remove();
this.cursor.classList.remove("active");
this.cursor.classList.remove('active');
this.pos = {
curr: null,
prev: null
prev: null,
};
this.pt = [];
@ -61,18 +58,18 @@ class Cursor {
}
init() {
document.onmousemove = e => {
(this.pos.curr == null) && this.move(e.clientX - 8, e.clientY - 8);
document.onmousemove = (e) => {
this.pos.curr == null && this.move(e.clientX - 8, e.clientY - 8);
this.pos.curr = {
x: e.clientX - 8,
y: e.clientY - 8
y: e.clientY - 8,
};
this.cursor.classList.remove("hidden");
this.cursor.classList.remove('hidden');
};
document.onmouseenter = e => this.cursor.classList.remove("hidden");
document.onmouseleave = e => this.cursor.classList.add("hidden");
document.onmousedown = e => this.cursor.classList.add("active");
document.onmouseup = e => this.cursor.classList.remove("active");
document.onmouseenter = (e) => this.cursor.classList.remove('hidden');
document.onmouseleave = (e) => this.cursor.classList.add('hidden');
document.onmousedown = (e) => this.cursor.classList.add('active');
document.onmouseup = (e) => this.cursor.classList.remove('active');
}
render() {

View File

@ -1,29 +1,16 @@
import { h } from "vue";
import { SpaCandle } from "@icon-park/vue-next";
import { h } from 'vue';
import { SpaCandle } from '@icon-park/vue-next';
// 时钟
export const getCurrentTime = () => {
let time = new Date();
let year = time.getFullYear();
let month =
time.getMonth() + 1 < 10
? "0" + (time.getMonth() + 1)
: time.getMonth() + 1;
let day = time.getDate() < 10 ? "0" + time.getDate() : time.getDate();
let hour = time.getHours() < 10 ? "0" + time.getHours() : time.getHours();
let minute =
time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();
let second =
time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
let weekday = [
"星期日",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
let month = time.getMonth() + 1 < 10 ? '0' + (time.getMonth() + 1) : time.getMonth() + 1;
let day = time.getDate() < 10 ? '0' + time.getDate() : time.getDate();
let hour = time.getHours() < 10 ? '0' + time.getHours() : time.getHours();
let minute = time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes();
let second = time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds();
let weekday = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
let currentTime = {
year,
month,
@ -87,21 +74,21 @@ export const helloInit = () => {
const hour = new Date().getHours();
let hello = null;
if (hour < 6) {
hello = "凌晨好";
hello = '凌晨好';
} else if (hour < 9) {
hello = "早上好";
hello = '早上好';
} else if (hour < 12) {
hello = "上午好";
hello = '上午好';
} else if (hour < 14) {
hello = "中午好";
hello = '中午好';
} else if (hour < 17) {
hello = "下午好";
hello = '下午好';
} else if (hour < 19) {
hello = "傍晚好";
hello = '傍晚好';
} else if (hour < 22) {
hello = "晚上好";
hello = '晚上好';
} else {
hello = "夜深了";
hello = '夜深了';
}
ElMessage({
dangerouslyUseHTMLString: true,
@ -111,11 +98,11 @@ export const helloInit = () => {
// 默哀模式
const anniversaries = {
4.4: "清明节",
5.12: "汶川大地震纪念日",
7.7: "中国人民抗日战争纪念日",
9.18: "九·一八事变纪念日",
12.13: "南京大屠杀死难者国家公祭日",
4.4: '清明节',
5.12: '汶川大地震纪念日',
7.7: '中国人民抗日战争纪念日',
9.18: '九·一八事变纪念日',
12.13: '南京大屠杀死难者国家公祭日',
};
export const checkDays = () => {
const myDate = new Date();
@ -124,13 +111,13 @@ export const checkDays = () => {
const key = `${mon}.${date}`;
if (anniversaries.hasOwnProperty(key)) {
console.log(`今天是${anniversaries[key]}`);
const gray = document.createElement("style");
gray.innerHTML = "html{filter: grayscale(100%)}";
const gray = document.createElement('style');
gray.innerHTML = 'html{filter: grayscale(100%)}';
document.head.appendChild(gray);
ElMessage({
message: `今天是${anniversaries[key]}`,
duration: 14000,
icon: h(SpaCandle, { theme: "filled", fill: "#efefef" }),
icon: h(SpaCandle, { theme: 'filled', fill: '#efefef' }),
});
}
};
@ -144,11 +131,11 @@ export const siteDateStatistics = (startDate) => {
const differenceInYears = differenceInMonths / 12;
if (differenceInYears >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInYears)}${Math.floor(
differenceInMonths % 12
differenceInMonths % 12,
)} ${Math.round(differenceInDays % 30)} `;
} else if (differenceInMonths >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInMonths)}${Math.round(
differenceInDays % 30
differenceInDays % 30,
)} `;
} else {
return `本站已经苟活了 ${Math.round(differenceInDays)}`;

View File

@ -1,9 +1,5 @@
<template>
<div
class="box cards"
@mouseenter="closeShow = true"
@mouseleave="closeShow = false"
>
<div class="box cards" @mouseenter="closeShow = true" @mouseleave="closeShow = false">
<transition name="el-fade-in-linear">
<close-one
class="close"
@ -31,9 +27,9 @@
</template>
<script setup>
import { CloseOne, SettingTwo } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import TimeCapsule from "@/components/TimeCapsule.vue";
import { CloseOne, SettingTwo } from '@icon-park/vue-next';
import { mainStore } from '@/store';
import TimeCapsule from '@/components/TimeCapsule.vue';
const store = mainStore();
const closeShow = ref(false);
@ -58,7 +54,9 @@ const closeShow = ref(false);
right: 14px;
width: 28px;
height: 28px;
transition: transform 0.3s, opacity 0.3s;
transition:
transform 0.3s,
opacity 0.3s;
&:hover {
transform: scale(1.2);

View File

@ -18,11 +18,7 @@
<span class="sm-hidden">{{ currentTime.weekday }}</span>
</div>
<div class="text">
<span>
{{ currentTime.hour }}:{{ currentTime.minute }}:{{
currentTime.second
}}</span
>
<span> {{ currentTime.hour }}:{{ currentTime.minute }}:{{ currentTime.second }}</span>
</div>
</div>
<Weather />
@ -33,11 +29,11 @@
</template>
<script setup>
import { getCurrentTime } from "@/utils/getTime";
import { mainStore } from "@/store";
import Music from "@/components/Music.vue";
import Hitokoto from "@/components/Hitokoto.vue";
import Weather from "@/components/Weather.vue";
import { getCurrentTime } from '@/utils/getTime';
import { mainStore } from '@/store';
import Music from '@/components/Music.vue';
import Hitokoto from '@/components/Hitokoto.vue';
import Weather from '@/components/Weather.vue';
const store = mainStore();
@ -126,7 +122,7 @@ onBeforeUnmount(() => {
margin-top: 10px;
font-size: 3.25rem;
letter-spacing: 2px;
font-family: "UnidreamLED";
font-family: 'UnidreamLED';
}
}
.weather {

View File

@ -6,9 +6,9 @@
</template>
<script setup>
import { mainStore } from "@/store";
import Message from "@/components/Message.vue";
import SocialLinks from "@/components/SocialLinks.vue";
import { mainStore } from '@/store';
import Message from '@/components/Message.vue';
import SocialLinks from '@/components/SocialLinks.vue';
const store = mainStore();
</script>

View File

@ -13,16 +13,15 @@
</template>
<script setup>
import { mainStore } from "@/store";
import Func from "@/views/Func/index.vue";
import Link from "@/components/Links.vue";
import { mainStore } from '@/store';
import Func from '@/views/Func/index.vue';
import Link from '@/components/Links.vue';
const store = mainStore();
//
const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
const siteUrl = import.meta.env.VITE_SITE_URL.split('.');
</script>
<style lang="scss" scoped>
.right {
// flex: 1 0 0%;
@ -30,7 +29,7 @@ const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
margin-left: 0.75rem;
.logo {
width: 100%;
font-family: "Pacifico-Regular";
font-family: 'Pacifico-Regular';
font-size: 1.75rem;
position: fixed;
top: 6%;

View File

@ -1,10 +1,5 @@
<template>
<div
class="set"
@mouseenter="closeShow = true"
@mouseleave="closeShow = false"
@click.stop
>
<div class="set" @mouseenter="closeShow = true" @mouseleave="closeShow = false" @click.stop>
<transition name="el-fade-in-linear">
<close-one
class="close"
@ -23,17 +18,8 @@
</div>
<div class="version">
<div class="num">v&nbsp;{{ config.version }}</div>
<el-tooltip
content="Github 源代码仓库"
placement="right"
:show-arrow="false"
>
<github-one
class="github"
theme="outline"
size="24"
@click="jumpTo(config.github)"
/>
<el-tooltip content="Github 源代码仓库" placement="right" :show-arrow="false">
<github-one class="github" theme="outline" size="24" @click="jumpTo(config.github)" />
</el-tooltip>
</div>
<el-card class="update">
@ -66,37 +52,26 @@
</template>
<script setup>
import {
CloseOne,
SettingTwo,
GithubOne,
AddOne,
Bug,
} from "@icon-park/vue-next";
import { mainStore } from "@/store";
import Set from "@/components/Set.vue";
import config from "@/../package.json";
import { CloseOne, SettingTwo, GithubOne, AddOne, Bug } from '@icon-park/vue-next';
import { mainStore } from '@/store';
import Set from '@/components/Set.vue';
import config from '@/../package.json';
const store = mainStore();
const closeShow = ref(false);
//
const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
const siteUrl = import.meta.env.VITE_SITE_URL.split('.');
//
const upData = reactive({
new: [
"采用 Vue 进行重构",
"音乐歌单支持快速自定义",
"壁纸支持个性化设置",
"音乐播放器支持音量控制",
],
fix: [
"修复天气 API",
"时光胶囊显示错误",
"移动端动画及细节",
"图标更换为 IconPark",
'采用 Vue 进行重构',
'音乐歌单支持快速自定义',
'壁纸支持个性化设置',
'音乐播放器支持音量控制',
],
fix: ['修复天气 API', '时光胶囊显示错误', '移动端动画及细节', '图标更换为 IconPark'],
});
//
@ -148,7 +123,7 @@ const jumpTo = (url) => {
.logo {
transform: translateY(-8%);
font-family: "Pacifico-Regular";
font-family: 'Pacifico-Regular';
// line-height: 5rem;
width: 100%;
height: 260px;
@ -170,7 +145,7 @@ const jumpTo = (url) => {
.num {
font-size: 2rem;
font-family: "Pacifico-Regular";
font-family: 'Pacifico-Regular';
}
.github {