ONI Agent Bridge - initial mod implementation
This commit is contained in:
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="contributors-container">
|
||||
<div v-for="user in contributors" :key="user.id" class="contributor-card">
|
||||
<a :href="user.html_url" target="_blank" rel="noreferrer">
|
||||
<img :src="user.avatar_url" :alt="user.login" class="avatar" />
|
||||
<span class="username">{{ user.login }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
repo: { type: String, required: true } // 格式: "Owner/RepoName"
|
||||
})
|
||||
|
||||
const contributors = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
const cacheKey = `contributors-${props.repo}`;
|
||||
const cachedData = localStorage.getItem(cacheKey);
|
||||
const cacheTime = localStorage.getItem(`${cacheKey}-timestamp`);
|
||||
|
||||
// 如果缓存存在且没超过 1 小时,直接用缓存
|
||||
if (cachedData && cacheTime && Date.now() - cacheTime < 3600000) {
|
||||
contributors.value = JSON.parse(cachedData);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/repos/${props.repo}/contributors`);
|
||||
const data = await response.json();
|
||||
if (Array.isArray(data)) {
|
||||
const users = data.filter(user => user.type === 'User');
|
||||
contributors.value = users;
|
||||
// 存入缓存
|
||||
localStorage.setItem(cacheKey, JSON.stringify(users));
|
||||
localStorage.setItem(`${cacheKey}-timestamp`, Date.now().toString());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取失败', e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contributors-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 24px;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.contributor-card a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
transition: transform 0.2s ease;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.contributor-card a:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
object-fit: cover;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 12px;
|
||||
margin-top: 8px;
|
||||
color: var(--vp-c-text-1);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item" v-for="(item, index) in steps" :key="index">
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-date">{{ item.status }}</div>
|
||||
<h4 class="timeline-title">{{ item.title }}</h4>
|
||||
<p class="timeline-desc">{{ item.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const steps = [
|
||||
{ status: '已完成', title: '摸鱼', desc: '了解上班摸鱼写文档的流程。' },
|
||||
{ status: '进行中', title: 'Mod 基础教程', desc: '掌握变量、类与对象,这是制作 Mod 的基石。' },
|
||||
{ status: '计划中', title: 'UI 界面开发', desc: '教你如何在游戏里画出自己的窗口和按钮。' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.timeline { margin: 2rem 0; padding-left: 20px; border-left: 2px solid var(--vp-c-divider); }
|
||||
.timeline-item { position: relative; margin-bottom: 2rem; padding-left: 30px; }
|
||||
.timeline-dot {
|
||||
position: absolute; left: -31px; top: 5px;
|
||||
width: 20px; height: 20px;
|
||||
background: var(--vp-c-brand);
|
||||
border: 4px solid var(--vp-c-bg);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.timeline-date { font-size: 0.8rem; color: var(--vp-c-brand); font-weight: bold; }
|
||||
.timeline-title { margin: 5px 0 !important; color: var(--vp-c-text-1); }
|
||||
.timeline-desc { font-size: 0.9rem; color: var(--vp-c-text-2); margin: 0; }
|
||||
</style>
|
||||
@ -0,0 +1,292 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
|
||||
const articles = ref([])
|
||||
const loading = ref(true)
|
||||
|
||||
const APP_ID = '457140'
|
||||
|
||||
// Tooltip 相关状态
|
||||
const tooltip = reactive({
|
||||
visible: false,
|
||||
lines: [],
|
||||
style: {}
|
||||
})
|
||||
|
||||
// 解析 Steam 补丁说明为纯文本行数组
|
||||
function parseSteamDescription(html) {
|
||||
if (!html) return []
|
||||
|
||||
return html
|
||||
.replace(/\r?\n/g, '')
|
||||
.replace(/<li>(.*?)<\/li>/gi, '• $1\n')
|
||||
.replace(/<\/p>|<br\s*\/?>/gi, '\n')
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.split('\n')
|
||||
.map(l => l.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
// 显示 Tooltip
|
||||
function showTooltip(e, item) {
|
||||
const rect = e.currentTarget.getBoundingClientRect()
|
||||
|
||||
const MAX_LINES = 10
|
||||
const GAP = 12
|
||||
const WIDTH = 700
|
||||
const LINE_HEIGHT = 22
|
||||
const PADDING = 24
|
||||
|
||||
const total = item.parsedLines.length
|
||||
|
||||
if (total > MAX_LINES) {
|
||||
tooltip.lines = [
|
||||
...item.parsedLines
|
||||
.slice(0, MAX_LINES - 1)
|
||||
.map(t => ({ text: t })),
|
||||
{ text: '......', ellipsis: true }
|
||||
]
|
||||
} else {
|
||||
tooltip.lines = item.parsedLines.map(t => ({ text: t }))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const estimatedHeight =
|
||||
tooltip.lines.length * LINE_HEIGHT + PADDING
|
||||
|
||||
let top
|
||||
|
||||
// 优先显示在上方
|
||||
if (rect.top < estimatedHeight + GAP) {
|
||||
top = rect.bottom + GAP
|
||||
} else {
|
||||
top = rect.top - estimatedHeight - GAP
|
||||
}
|
||||
|
||||
tooltip.style = {
|
||||
position: 'fixed',
|
||||
top: `${top}px`,
|
||||
left: `${rect.left}px`,
|
||||
width: `${WIDTH}px`
|
||||
}
|
||||
|
||||
tooltip.visible = true
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.visible = false
|
||||
}
|
||||
|
||||
|
||||
// 获取 Steam 新闻数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fstore.steampowered.com%2Ffeeds%2Fnews%2Fapp%2F${APP_ID}`
|
||||
)
|
||||
const data = await res.json()
|
||||
|
||||
if (data.status === 'ok') {
|
||||
articles.value = data.items.slice(0, 10).map(item => {
|
||||
const imgReg = /<img.*?src=["'](.*?)["']/
|
||||
const match = item.content?.match(imgReg)
|
||||
|
||||
const fallbackImg =
|
||||
`https://cdn.akamai.steamstatic.com/steam/apps/${APP_ID}/header.jpg`
|
||||
|
||||
return {
|
||||
...item,
|
||||
displayImage: (match ? match[1] : item.thumbnail) || fallbackImg,
|
||||
displayDate: item.pubDate.split(' ')[0],
|
||||
parsedLines: parseSteamDescription(item.description)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<template>
|
||||
<div class="steam-container">
|
||||
<div v-if="loading" class="loading">
|
||||
正在同步 Steam 补丁说明...
|
||||
</div>
|
||||
|
||||
<div v-else class="news-list">
|
||||
<a
|
||||
v-for="item in articles"
|
||||
:key="item.guid"
|
||||
:href="item.link"
|
||||
target="_blank"
|
||||
class="news-card"
|
||||
@mouseenter="e => showTooltip(e, item)"
|
||||
@mouseleave="hideTooltip"
|
||||
>
|
||||
<div class="news-card-inner">
|
||||
<div class="card-text">
|
||||
<div class="card-header">
|
||||
<span class="tag">游戏更新</span>
|
||||
<span class="date">{{ item.displayDate }}</span>
|
||||
</div>
|
||||
|
||||
<h3 class="title">{{ item.title }}</h3>
|
||||
|
||||
<p class="description">
|
||||
{{ item.parsedLines.join(' ').slice(0, 60) }}...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-image-box">
|
||||
<img :src="item.displayImage" />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="tooltip.visible"
|
||||
class="global-tooltip"
|
||||
:style="tooltip.style"
|
||||
>
|
||||
<div
|
||||
v-for="(line, i) in tooltip.lines"
|
||||
:key="i"
|
||||
class="tooltip-line"
|
||||
:class="{
|
||||
bullet: line.text.startsWith('•'),
|
||||
ellipsis: line.ellipsis
|
||||
}"
|
||||
>
|
||||
{{ line.text }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
.news-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.news-card {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.news-card-inner {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: #2a313d;
|
||||
border: 1px solid #535a66;
|
||||
border-radius: 4px;
|
||||
transition: transform 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.news-card:hover .news-card-inner {
|
||||
background: #363f4c;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.card-text {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #8f98a0;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #66c0f4;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 6px 0 0;
|
||||
font-size: 1.1rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.85rem;
|
||||
color: #acb2b8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.card-image-box {
|
||||
width: 200px;
|
||||
height: 112px;
|
||||
margin: 0.75rem;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card-image-box img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.global-tooltip {
|
||||
background: rgba(18, 22, 28, 0.96);
|
||||
border: 1px solid #3d4450;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
width: 700px;
|
||||
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);
|
||||
z-index: 9999999;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tooltip-line {
|
||||
font-size: 0.8rem;
|
||||
color: #dcdedf;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.tooltip-line.bullet {
|
||||
padding-left: 14px;
|
||||
text-indent: -14px;
|
||||
color: #c7d5e0;
|
||||
}
|
||||
|
||||
.tooltip-line.ellipsis {
|
||||
color: #66c0f4;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,290 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const mods = ref([])
|
||||
const loading = ref(true)
|
||||
|
||||
const STATS_CACHE_TTL = 60 * 60 * 1000 // 60 分钟
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
function getCachedStats(id) {
|
||||
const raw = localStorage.getItem('workshop_stats_' + id)
|
||||
if (!raw) return null
|
||||
|
||||
const { time, data } = JSON.parse(raw)
|
||||
if (Date.now() - time > STATS_CACHE_TTL) return null
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
function setCachedStats(id, data) {
|
||||
localStorage.setItem(
|
||||
'workshop_stats_' + id,
|
||||
JSON.stringify({
|
||||
time: Date.now(),
|
||||
data
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function fetchWorkshopStats(detailUrl) {
|
||||
try {
|
||||
const match = detailUrl.match(/id=(\d+)/)
|
||||
if (!match) return null
|
||||
|
||||
const cleanUrl = `https://steamcommunity.com/sharedfiles/filedetails/?id=${match[1]}`
|
||||
|
||||
const cached = getCachedStats(match[1])
|
||||
if (cached) return cached
|
||||
|
||||
const workerUrl =
|
||||
`https://aged-dream-7a55.liukele015.workers.dev/?url=${encodeURIComponent(cleanUrl)}`
|
||||
|
||||
const res = await fetch(workerUrl)
|
||||
if (!res.ok) {
|
||||
console.warn('Worker 返回失败:', res.status, res.statusText)
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
if (!data || !data.stats) {
|
||||
console.warn('Worker 返回数据不正确:', data)
|
||||
return null
|
||||
}
|
||||
|
||||
setCachedStats(match[1], data.stats)
|
||||
return data.stats
|
||||
|
||||
} catch (err) {
|
||||
console.warn('统计获取失败:', detailUrl, err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStats(mod) {
|
||||
if (!mod.link || mod.link === '#') return
|
||||
if (mod.stats || mod.statsLoading) return
|
||||
|
||||
mod.statsLoading = true
|
||||
mod.stats = await fetchWorkshopStats(mod.link)
|
||||
mod.statsLoading = false
|
||||
}
|
||||
|
||||
async function loadStatsSequentially(list) {
|
||||
for (const mod of list) {
|
||||
await loadStats(mod)
|
||||
await sleep(150)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const steamUrl =
|
||||
'https://steamcommunity.com/workshop/browse/' +
|
||||
'?appid=457140' +
|
||||
'&browssemethod=trend' +
|
||||
'§ion=readytouseitems' +
|
||||
'&actualsort=trend' +
|
||||
'&p=1' +
|
||||
'&numperpage=6'
|
||||
|
||||
// 这里使用 Cloudflare做为代理,避免 CORS 问题
|
||||
const proxyUrl =
|
||||
`https://aged-dream-7a55.liukele015.workers.dev/?mode=workshop&url=${encodeURIComponent(steamUrl)}`
|
||||
|
||||
const res = await fetch(proxyUrl)
|
||||
const html = await res.text()
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, 'text/html')
|
||||
const items = doc.querySelectorAll('.workshopItem')
|
||||
|
||||
mods.value = Array.from(items).map(item => {
|
||||
const titleEl = item.querySelector('.workshopItemTitle')
|
||||
const linkEl = item.querySelector('a')
|
||||
const authorEl = item.querySelector('.workshopItemAuthorName a')
|
||||
|
||||
const imgDiv = item.querySelector('.workshopItemImg')
|
||||
const bg = imgDiv?.style?.backgroundImage || ''
|
||||
const bgUrl = bg.match(/url\(["']?(.*?)["']?\)/)?.[1] || null
|
||||
|
||||
const imgPreview = item.querySelector('img.preview_image')
|
||||
const previewUrl = imgPreview?.src || null
|
||||
|
||||
const imgAny = item.querySelector('img')
|
||||
const anyUrl = imgAny?.src || null
|
||||
|
||||
let thumbnail =
|
||||
bgUrl || previewUrl || anyUrl ||
|
||||
'https://via.placeholder.com/200x120?text=No+Image'
|
||||
|
||||
if (thumbnail.startsWith('//')) {
|
||||
thumbnail = 'https:' + thumbnail
|
||||
}
|
||||
|
||||
thumbnail = `https://images.weserv.nl/?url=${encodeURIComponent(thumbnail)}`
|
||||
|
||||
return {
|
||||
title: titleEl?.innerText.trim() ?? '未知模组',
|
||||
link: linkEl?.href ?? '#',
|
||||
author: authorEl?.innerText.trim() ?? '匿名作者',
|
||||
thumbnail,
|
||||
|
||||
stats: null,
|
||||
statsLoading: false
|
||||
}
|
||||
})
|
||||
|
||||
loading.value = false
|
||||
loadStatsSequentially(mods.value)
|
||||
|
||||
} catch (err) {
|
||||
console.error('抓取 Workshop 失败:', err)
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="workshop-container">
|
||||
<div v-if="loading" class="loading-box">
|
||||
正在连接 Steam 创意工坊...
|
||||
</div>
|
||||
|
||||
<div v-else class="mod-grid">
|
||||
<a
|
||||
v-for="mod in mods"
|
||||
:key="mod.link"
|
||||
:href="mod.link"
|
||||
target="_blank"
|
||||
class="mod-card"
|
||||
>
|
||||
<div class="mod-img">
|
||||
<img :src="mod.thumbnail" loading="lazy" />
|
||||
</div>
|
||||
|
||||
<div class="mod-card__body">
|
||||
<div class="mod-card__title">{{ mod.title }}</div>
|
||||
<div class="mod-card__meta">By {{ mod.author }}</div>
|
||||
|
||||
<div class="mod-card__stats">
|
||||
<template v-if="mod.stats">
|
||||
<span>访客 {{ mod.stats.visitors }}</span>
|
||||
<span>订阅 {{ mod.stats.subscribers }}</span>
|
||||
<span>收藏 {{ mod.stats.favorites }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="mod.statsLoading">
|
||||
<span>统计加载中…</span>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<span>—</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workshop-container {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--vp-c-text-2);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.mod-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.mod-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.mod-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.mod-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.mod-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-4px);
|
||||
background: var(--vp-c-bg-mute);
|
||||
}
|
||||
|
||||
.mod-img {
|
||||
width: 100%;
|
||||
height: 125px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.mod-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.mod-card__body {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mod-card__title {
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.3;
|
||||
height: 2.6em;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.mod-card__meta {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.8rem;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.mod-card__stats {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.mod-card__stats span {
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* VitePress 布局宽度自定义
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* 1. 文档内容的最大宽度 (默认是 1152px) */
|
||||
/* 设置为 1280px 或 1440px 可以让代码块更宽,减少横向滚动条 */
|
||||
--vp-content-max-width: 1280px;
|
||||
|
||||
/* 2. 整个页面的最大宽度 (包括侧边栏和正文) */
|
||||
--vp-layout-max-width: 1600px;
|
||||
|
||||
/* --vp-c-brand-1: #8f2a3b;
|
||||
--vp-c-brand-2: #8f2a3b; */
|
||||
}
|
||||
|
||||
|
||||
.VPHero .name {
|
||||
/* 撤销默认的渐变色背景 */
|
||||
background: none !important;
|
||||
-webkit-background-clip: initial !important;
|
||||
background-clip: initial !important;
|
||||
-webkit-text-fill-color: initial !important;
|
||||
}
|
||||
|
||||
|
||||
/* 强制覆盖首页标题的渐变效果 */
|
||||
.VPHero .name span.color-w {
|
||||
background: none !important;
|
||||
-webkit-text-fill-color: initial !important;
|
||||
color: var(--vp-c-text-1) !important;
|
||||
}
|
||||
|
||||
.VPHero .name span.color-r {
|
||||
background: none !important;
|
||||
-webkit-text-fill-color: #ff4d4f !important; /* 红色 */
|
||||
color: #ff4d4f !important;
|
||||
}
|
||||
|
||||
/* 正文里的颜色 */
|
||||
span.color-white {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
span.color-white {
|
||||
color: #ff4d4f !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 如果你希望“代码块”在宽屏下表现更好,可以加上这个微调 */
|
||||
@media (min-width: 1280px) {
|
||||
.VPDoc .container {
|
||||
/* 这里的 margin 会自动平衡左右空间 */
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. (可选) 首页 Hero 部分的宽度调整 */
|
||||
:root {
|
||||
--vp-home-hero-max-width: 1152px;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
// .vitepress/theme/index.mts
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './custom.css' // 这里的引入才是有效的
|
||||
// @ts-ignore
|
||||
import Contributors from './components/Contributors.vue'
|
||||
import SteamNews from './components/SteamNews.vue'
|
||||
import LearningTimeline from './components/LearningTimeline.vue'
|
||||
import WorkshopList from './components/WorkshopList.vue'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp({ app }: any) {
|
||||
app.component('Contributors', Contributors)
|
||||
app.component('SteamNews', SteamNews)
|
||||
app.component('LearningTimeline', LearningTimeline)
|
||||
app.component('WorkshopList', WorkshopList)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user