improvement(wrapped-ui): 移除 VHS 主题并优化 DOS/CRT 视觉效果

- 主题系统收敛为 Modern/Game Boy/DOS(快捷键改为 F1-F3)
- 删除 VHS 切换器与相关样式(卡片/控件/年份选择/图表等)
- DOS 主题统一使用像素字体,减弱发光强度并细化扫描线/闪烁参数
- DOS 闪烁光标改由 WrappedCRTOverlay 渲染,避免全局样式副作用
- 移除热力图 vhs 配色分支
This commit is contained in:
2977094657
2026-02-01 19:27:51 +08:00
parent 52ada9da64
commit 7ce6abecca
17 changed files with 81 additions and 942 deletions

View File

@@ -1,62 +1,38 @@
<template>
<!-- CRT/VHS 滤镜叠加层 - 根据主题切换效果 -->
<!-- CRT 滤镜叠加层 - 复古主题使用 -->
<div class="absolute inset-0 pointer-events-none select-none z-30" aria-hidden="true">
<!-- Game Boy / DOS: 扫描线层 -->
<div v-if="theme !== 'vhs'" class="absolute inset-0 crt-scanlines"></div>
<!-- Game Boy / DOS: RGB 子像素层 -->
<div v-if="theme !== 'vhs'" class="absolute inset-0 crt-rgb-pixels"></div>
<!-- Game Boy / DOS: 闪烁层 -->
<div v-if="theme !== 'vhs'" class="absolute inset-0 crt-flicker"></div>
<!-- 共享: 暗角层 -->
<!-- 扫描线 / RGB 子像素 / 闪烁 / 暗角 / 曲率 -->
<div class="absolute inset-0 crt-scanlines"></div>
<div class="absolute inset-0 crt-rgb-pixels"></div>
<div class="absolute inset-0 crt-flicker"></div>
<div class="absolute inset-0 crt-vignette"></div>
<div class="absolute inset-0 crt-curvature"></div>
<!-- Game Boy / DOS: 屏幕曲率层 -->
<div v-if="theme !== 'vhs'" class="absolute inset-0 crt-curvature"></div>
<!-- VHS: 跟踪线效果 -->
<div v-if="theme === 'vhs'" class="vhs-tracking"></div>
<!-- VHS: REC 指示器 -->
<div v-if="theme === 'vhs'" class="vhs-rec">REC</div>
<!-- VHS: 时间戳 -->
<div v-if="theme === 'vhs'" class="vhs-timestamp">{{ vhsTimestamp }}</div>
<!-- DOS: 语义化光标 -->
<div v-if="theme === 'dos'" class="dos-cursor"></div>
</div>
</template>
<script setup>
// CRT/VHS 滤镜叠加层组件
// 根据当前主题切换不同的视觉效果
const { theme } = useWrappedTheme()
</script>
// VHS 时间戳(实时更新)
const vhsTimestamp = ref('')
const updateTimestamp = () => {
const now = new Date()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const year = now.getFullYear()
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
vhsTimestamp.value = `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`
<style scoped>
/* DOS 语义化光标 */
.dos-cursor {
position: fixed;
bottom: 20px;
right: 20px;
color: #33ff33;
font-size: 1.5rem;
font-family: var(--font-pixel-10), 'Courier New', monospace;
text-shadow: 0 0 8px rgba(51, 255, 51, 0.6);
animation: dos-cursor-blink 530ms steps(1) infinite;
z-index: 100;
}
let timestampInterval = null
onMounted(() => {
updateTimestamp()
timestampInterval = setInterval(updateTimestamp, 1000)
})
onUnmounted(() => {
if (timestampInterval) {
clearInterval(timestampInterval)
}
})
</script>
@keyframes dos-cursor-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
</style>

View File

@@ -118,39 +118,4 @@ defineProps({
.wrapped-theme-dos .border-\[\#F3F3F3\] {
border-color: #33ff33 !important;
}
/* ========== VHS 主题 ========== */
/* 卡片背景 */
.wrapped-theme-vhs .bg-white {
background: #16213e !important;
border-color: #0f3460 !important;
}
/* 标题 */
.wrapped-theme-vhs .wrapped-title {
color: #eaeaea !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.4),
1px 0 rgba(255, 0, 255, 0.4);
}
/* 描述文字 */
.wrapped-theme-vhs .wrapped-body {
color: #a0a0a0 !important;
}
/* 数字高亮 */
.wrapped-theme-vhs .wrapped-number {
color: #e94560 !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.5),
1px 0 rgba(255, 0, 255, 0.5);
}
/* 边框 */
.wrapped-theme-vhs .border-\[\#EDEDED\],
.wrapped-theme-vhs .border-\[\#F3F3F3\] {
border-color: #0f3460 !important;
}
</style>

View File

@@ -154,28 +154,4 @@ const yearOptions = computed(() => {
.wrapped-theme-dos .controls-btn:hover:not(:disabled) {
background-color: #44ff44;
}
/* VHS 特殊样式 */
.wrapped-theme-vhs .controls-panel {
border-radius: 4px;
background: linear-gradient(180deg, #16213e 0%, #1a1a2e 100%);
border-color: #0f3460;
}
.wrapped-theme-vhs .controls-select {
border-radius: 2px;
background: #1a1a2e;
border-color: #0f3460;
color: #eaeaea;
}
.wrapped-theme-vhs .controls-btn {
border-radius: 4px;
background: linear-gradient(135deg, #e94560 0%, #0f3460 100%);
color: #ffffff;
}
.wrapped-theme-vhs .controls-btn:hover:not(:disabled) {
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.5);
}
</style>

View File

@@ -10,8 +10,7 @@ const themeSwitcherComponent = computed(() => {
const map = {
off: resolveComponent('WrappedThemeSwitcherModern'),
gameboy: resolveComponent('WrappedThemeSwitcherGameboy'),
dos: resolveComponent('WrappedThemeSwitcherDos'),
vhs: resolveComponent('WrappedThemeSwitcherVhs')
dos: resolveComponent('WrappedThemeSwitcherDos')
}
return map[theme.value] || map.off
})

View File

@@ -15,7 +15,7 @@
</div>
<!-- 状态提示 -->
<div class="dos-status">
Press F1-F4 to switch theme
Press F1-F3 to switch theme
</div>
</div>
</template>
@@ -26,8 +26,7 @@ const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'Modern' },
{ value: 'gameboy', label: 'GameBoy' },
{ value: 'dos', label: 'DOS' },
{ value: 'vhs', label: 'VHS' }
{ value: 'dos', label: 'DOS' }
]
</script>

View File

@@ -25,8 +25,7 @@ const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'MODERN' },
{ value: 'gameboy', label: 'GAME BOY' },
{ value: 'dos', label: 'DOS' },
{ value: 'vhs', label: 'VHS' }
{ value: 'dos', label: 'DOS' }
]
const selectTheme = (value) => {

View File

@@ -20,12 +20,11 @@
</template>
<script setup>
const { theme, setTheme, VALID_THEMES } = useWrappedTheme()
const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'Modern' },
{ value: 'gameboy', label: 'Game Boy' },
{ value: 'dos', label: 'DOS' },
{ value: 'vhs', label: 'VHS' }
{ value: 'dos', label: 'DOS' }
]
</script>

View File

@@ -1,126 +0,0 @@
<template>
<div class="vhs-panel select-none">
<!-- VHS 风格物理按钮组 -->
<div class="vhs-button-group">
<button
v-for="t in themes"
:key="t.value"
class="vhs-button"
:class="{ 'is-active': theme === t.value, 'is-pressed': pressedKey === t.value }"
@mousedown="pressButton(t.value)"
@mouseup="releaseButton"
@mouseleave="releaseButton"
@click="setTheme(t.value)"
>
<span class="vhs-button-face">{{ t.label }}</span>
<span class="vhs-led" :class="{ 'is-on': theme === t.value }"></span>
</button>
</div>
</div>
</template>
<script setup>
const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'MOD' },
{ value: 'gameboy', label: 'GB' },
{ value: 'dos', label: 'DOS' },
{ value: 'vhs', label: 'VHS' }
]
const pressedKey = ref(null)
const pressButton = (value) => {
pressedKey.value = value
}
const releaseButton = () => {
pressedKey.value = null
}
</script>
<style scoped>
.vhs-panel {
font-family: 'Arial', 'Helvetica', sans-serif;
}
.vhs-button-group {
display: flex;
gap: 6px;
padding: 8px 10px;
background: linear-gradient(180deg, #2a2a3e 0%, #1a1a2e 100%);
border-radius: 4px;
border: 1px solid #3a3a5e;
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.1),
0 2px 4px rgba(0,0,0,0.3);
}
.vhs-button {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 0;
background: none;
border: none;
cursor: pointer;
}
.vhs-button-face {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 24px;
font-size: 9px;
font-weight: bold;
letter-spacing: 0.5px;
color: #cccccc;
background: linear-gradient(180deg, #4a4a5e 0%, #2a2a3e 50%, #3a3a4e 100%);
border: 1px solid #5a5a7e;
border-radius: 3px;
box-shadow:
0 2px 0 #1a1a2e,
inset 0 1px 0 rgba(255,255,255,0.2);
transition: all 0.05s;
}
.vhs-button:hover .vhs-button-face {
background: linear-gradient(180deg, #5a5a6e 0%, #3a3a4e 50%, #4a4a5e 100%);
}
.vhs-button.is-pressed .vhs-button-face,
.vhs-button:active .vhs-button-face {
transform: translateY(2px);
box-shadow:
0 0 0 #1a1a2e,
inset 0 1px 2px rgba(0,0,0,0.3);
background: linear-gradient(180deg, #3a3a4e 0%, #2a2a3e 50%, #3a3a4e 100%);
}
.vhs-button.is-active .vhs-button-face {
background: linear-gradient(135deg, #e94560 0%, #c73e54 50%, #0f3460 100%);
color: #ffffff;
border-color: #e94560;
}
.vhs-led {
width: 6px;
height: 6px;
border-radius: 50%;
background: #1a1a1a;
border: 1px solid #3a3a3a;
transition: all 0.2s;
}
.vhs-led.is-on {
background: #ff3333;
border-color: #ff6666;
box-shadow:
0 0 4px #ff3333,
0 0 8px #ff3333,
0 0 12px rgba(255,51,51,0.5);
}
</style>

View File

@@ -1,34 +1,7 @@
<template>
<div class="year-selector" :class="selectorClass">
<!-- Modern 风格下拉菜单 -->
<div v-if="theme === 'off'" class="year-modern">
<div class="relative inline-flex items-center">
<select
class="appearance-none bg-transparent pr-5 pl-0 py-0.5 rounded-md wrapped-label text-xs text-[#00000066] text-right focus:outline-none focus-visible:ring-2 focus-visible:ring-[#07C160]/30 hover:bg-[#000000]/5 transition disabled:opacity-70 disabled:cursor-default"
:disabled="years.length <= 1"
:value="String(modelValue)"
@change="onSelectChange"
>
<option v-for="y in years" :key="y" :value="String(y)">{{ y }}</option>
</select>
<svg
v-if="years.length > 1"
class="pointer-events-none absolute right-1 w-3 h-3 text-[#00000066]"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.24a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
<!-- Game Boy 风格 -->
<div v-else-if="theme === 'gameboy'" class="year-gameboy">
<div v-if="theme === 'gameboy'" class="year-gameboy">
<div class="gameboy-year-box">
<button
class="gameboy-arrow"
@@ -65,27 +38,31 @@
>[+]</button>
</div>
<!-- VHS 风格 -->
<div v-else-if="theme === 'vhs'" class="year-vhs">
<button
class="vhs-transport-btn"
:disabled="!canGoPrev"
@click="prevYear"
aria-label="Previous year"
>
<span class="vhs-icon"></span>
</button>
<div class="vhs-led-display">
<span class="vhs-led-digit">{{ modelValue }}</span>
<!-- Modern 风格下拉菜单默认 -->
<div v-else class="year-modern">
<div class="relative inline-flex items-center">
<select
class="appearance-none bg-transparent pr-5 pl-0 py-0.5 rounded-md wrapped-label text-xs text-[#00000066] text-right focus:outline-none focus-visible:ring-2 focus-visible:ring-[#07C160]/30 hover:bg-[#000000]/5 transition disabled:opacity-70 disabled:cursor-default"
:disabled="years.length <= 1"
:value="String(modelValue)"
@change="onSelectChange"
>
<option v-for="y in years" :key="y" :value="String(y)">{{ y }}</option>
</select>
<svg
v-if="years.length > 1"
class="pointer-events-none absolute right-1 w-3 h-3 text-[#00000066]"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M5.23 7.21a.75.75 0 0 1 1.06.02L10 10.94l3.71-3.71a.75.75 0 1 1 1.06 1.06l-4.24 4.24a.75.75 0 0 1-1.06 0L5.21 8.29a.75.75 0 0 1 .02-1.08z"
clip-rule="evenodd"
/>
</svg>
</div>
<button
class="vhs-transport-btn"
:disabled="!canGoNext"
@click="nextYear"
aria-label="Next year"
>
<span class="vhs-icon"></span>
</button>
</div>
</div>
</template>
@@ -263,72 +240,4 @@ onBeforeUnmount(() => {
min-width: 50px;
text-align: center;
}
/* ========== VHS 风格 ========== */
.year-vhs {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
background: linear-gradient(180deg, #2a2a3e 0%, #1a1a2e 100%);
border-radius: 4px;
border: 1px solid #3a3a5e;
}
.vhs-transport-btn {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 22px;
background: linear-gradient(180deg, #4a4a5e 0%, #2a2a3e 50%, #3a3a4e 100%);
border: 1px solid #5a5a7e;
border-radius: 3px;
color: #cccccc;
font-size: 8px;
cursor: pointer;
box-shadow:
0 2px 0 #1a1a2e,
inset 0 1px 0 rgba(255,255,255,0.2);
transition: all 0.05s;
}
.vhs-transport-btn:hover:not(:disabled) {
background: linear-gradient(180deg, #5a5a6e 0%, #3a3a4e 50%, #4a4a5e 100%);
}
.vhs-transport-btn:active:not(:disabled) {
transform: translateY(2px);
box-shadow:
0 0 0 #1a1a2e,
inset 0 1px 2px rgba(0,0,0,0.3);
}
.vhs-transport-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.vhs-icon {
letter-spacing: -2px;
}
.vhs-led-display {
background: #0a0a0a;
border: 1px solid #3a3a3a;
padding: 4px 10px;
border-radius: 2px;
box-shadow: inset 0 1px 3px rgba(0,0,0,0.5);
}
.vhs-led-digit {
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: bold;
color: #ff3333;
text-shadow:
0 0 4px #ff3333,
0 0 8px #ff3333;
letter-spacing: 2px;
}
</style>

View File

@@ -391,19 +391,4 @@ watch(
font-family: 'Courier New', monospace;
text-shadow: 0 0 3px rgba(51, 255, 51, 0.6);
}
/* ========== VHS 主题 ========== */
.wrapped-theme-vhs .wrapped-chat-replay {
--wr-chat-frame-bg: #16213e;
--wr-chat-top-bg: #0f3460;
--wr-chat-chat-bg: #1a1a2e;
--wr-chat-border: #0f3460;
--wr-chat-bubble-bg: linear-gradient(135deg, #e94560 0%, #0f3460 100%);
--wr-chat-bubble-tail: #0f3460;
--wr-chat-bubble-text: #ffffff;
--wr-chat-typing-bg: rgba(255, 255, 255, 0.08);
--wr-chat-typing-dot: rgba(255, 255, 255, 0.85);
}
</style>

View File

@@ -377,56 +377,4 @@ const labels = computed(() => {
color: #33ff33 !important;
text-shadow: 0 0 5px #33ff33;
}
/* ========== VHS 主题 ========== */
.wrapped-theme-vhs .overview-card {
background: #16213e !important;
border: 1px solid #0f3460 !important;
backdrop-filter: none;
}
.wrapped-theme-vhs .overview-grid-line {
stroke: #0f3460;
stroke-opacity: 0.6;
}
.wrapped-theme-vhs .overview-axis-line {
stroke: #0f3460;
stroke-opacity: 0.8;
}
.wrapped-theme-vhs .overview-data-polygon {
fill: rgba(233, 69, 96, 0.2);
stroke: #e94560;
}
.wrapped-theme-vhs .overview-data-node {
fill: #e94560;
stroke: #16213e;
}
.wrapped-theme-vhs .overview-label {
fill: #a0a0a0;
}
.wrapped-theme-vhs .overview-progress-bg {
background: #0f3460 !important;
}
.wrapped-theme-vhs .overview-progress-fill {
background: linear-gradient(90deg, #e94560, #0f3460) !important;
}
.wrapped-theme-vhs .wrapped-label,
.wrapped-theme-vhs .wrapped-body {
color: #a0a0a0 !important;
}
.wrapped-theme-vhs .wrapped-number {
color: #e94560 !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.5),
1px 0 rgba(255, 0, 255, 0.5);
}
</style>

View File

@@ -1321,218 +1321,6 @@ const getLabelStyle = (code) => {
text-shadow: 0 0 5px #ff3333;
}
/* ========== VHS 录像带主题 - 复古视频风格键盘 ========== */
.wrapped-theme-vhs .keyboard-outer {
border-radius: 4px;
background: linear-gradient(180deg, #2a2a4e 0%, #1a1a2e 100%);
border: 2px solid #0f3460;
padding: 4px;
box-shadow:
0 4px 20px rgba(233, 69, 96, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.wrapped-theme-vhs .keyboard-inner {
border-radius: 2px;
background: linear-gradient(180deg, #16213e 0%, #0f0f23 100%);
border: 1px solid #0f3460;
padding: 6px;
}
.wrapped-theme-vhs .keyboard-header {
border-bottom: 1px solid #0f3460;
padding-bottom: 6px;
margin-bottom: 6px;
}
.wrapped-theme-vhs .dot {
border-radius: 50%;
width: 8px;
height: 8px;
}
.wrapped-theme-vhs .dot-red {
background: #e94560;
box-shadow: 0 0 8px #e94560;
animation: vhs-dot-flicker 0.1s infinite;
}
.wrapped-theme-vhs .dot-yellow { background: #f39c12; box-shadow: 0 0 5px #f39c12; }
.wrapped-theme-vhs .dot-green { background: #27ae60; box-shadow: 0 0 5px #27ae60; }
@keyframes vhs-dot-flicker {
0%, 90%, 100% { opacity: 1; }
92% { opacity: 0.7; }
95% { opacity: 1; }
97% { opacity: 0.8; }
}
.wrapped-theme-vhs .keyboard-stats,
.wrapped-theme-vhs .keyboard-hint {
font-family: 'Courier New', monospace;
color: #eaeaea;
text-shadow:
-1px 0 #00fff7,
1px 0 #ff00ff;
}
.wrapped-theme-vhs .keyboard-body {
border-radius: 2px;
background: #0a0a1a;
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.5);
}
.wrapped-theme-vhs .kb-key::before {
border-radius: 2px;
background: #1a1a2e;
}
.wrapped-theme-vhs .kb-key-top {
border-radius: 2px;
border: 1px solid #0f3460 !important;
background: linear-gradient(180deg, #2a2a4e 0%, #1a1a2e 100%) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.1),
0 2px 4px rgba(0, 0, 0, 0.3) !important;
}
.wrapped-theme-vhs .kb-label {
font-family: 'Courier New', monospace;
color: #eaeaea !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.5),
1px 0 rgba(255, 0, 255, 0.5);
filter: none !important;
}
.wrapped-theme-vhs .kb-sub {
font-family: 'Courier New', monospace;
color: #a0a0a0 !important;
filter: none !important;
}
.wrapped-theme-vhs .kb-space-bar {
border-radius: 2px;
background: linear-gradient(90deg, #e94560 0%, #0f3460 100%);
height: 3px;
}
.wrapped-theme-vhs .keyboard-brand {
font-family: 'Courier New', monospace;
color: #e94560;
text-shadow:
-1px 0 #00fff7,
1px 0 #ff00ff;
letter-spacing: 3px;
}
/* VHS 磨损效果 - 色彩溢出增强 */
.wrapped-theme-vhs .kb-level-1 .kb-label,
.wrapped-theme-vhs .kb-level-2 .kb-label {
text-shadow:
-1px 0 rgba(0, 255, 247, 0.6),
1px 0 rgba(255, 0, 255, 0.6);
}
.wrapped-theme-vhs .kb-level-3 .kb-label,
.wrapped-theme-vhs .kb-level-4 .kb-label {
text-shadow:
-2px 0 rgba(0, 255, 247, 0.7),
2px 0 rgba(255, 0, 255, 0.7);
}
.wrapped-theme-vhs .kb-level-5 .kb-label,
.wrapped-theme-vhs .kb-level-6 .kb-label {
text-shadow:
-2px 0 rgba(0, 255, 247, 0.8),
2px 0 rgba(255, 0, 255, 0.8);
animation: vhs-text-glitch 3s infinite;
}
.wrapped-theme-vhs .kb-level-7 .kb-key-top,
.wrapped-theme-vhs .kb-level-8 .kb-key-top {
animation: vhs-key-glitch 2s infinite;
}
.wrapped-theme-vhs .kb-level-9 .kb-key-top {
animation: vhs-key-glitch 1s infinite;
border-color: #e94560 !important;
}
@keyframes vhs-text-glitch {
0%, 95%, 100% { transform: translateX(0); }
96% { transform: translateX(-1px); }
97% { transform: translateX(1px); }
98% { transform: translateX(-1px); }
99% { transform: translateX(0); }
}
@keyframes vhs-key-glitch {
0%, 92%, 100% {
transform: translate(0);
filter: none;
}
93% {
transform: translate(-1px, 0);
filter: hue-rotate(90deg);
}
95% {
transform: translate(1px, 0);
filter: hue-rotate(-90deg);
}
97% {
transform: translate(0, -1px);
filter: saturate(1.5);
}
}
.wrapped-theme-vhs .kb-level-10 .kb-key-top {
opacity: 0 !important;
}
.wrapped-theme-vhs .kb-level-10::before {
background: linear-gradient(
45deg,
#1a1a2e 25%,
#0f3460 25%,
#0f3460 50%,
#1a1a2e 50%,
#1a1a2e 75%,
#0f3460 75%
) !important;
background-size: 4px 4px;
animation: vhs-static 0.5s infinite;
}
@keyframes vhs-static {
0% { background-position: 0 0; }
100% { background-position: 4px 4px; }
}
.wrapped-theme-vhs .kb-level-10::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 6px;
height: 6px;
background: #e94560;
border-radius: 50%;
box-shadow: 0 0 10px #e94560;
animation: vhs-dot-flicker 0.2s infinite;
}
/* VHS 聊天气泡主题适配 */
.wrapped-theme-vhs .bubble-left,
.wrapped-theme-vhs .bubble-right {
background: #16213e;
border: 1px solid #0f3460;
}
.wrapped-theme-vhs .bubble-left::before {
border-right-color: #16213e;
}
.wrapped-theme-vhs .bubble-right {
background: linear-gradient(135deg, #e94560 0%, #0f3460 100%);
}
.wrapped-theme-vhs .bubble-right::after {
border-left-color: #0f3460;
}
/* DOS 聊天气泡主题适配 */
.wrapped-theme-dos .bubble-left,
.wrapped-theme-dos .bubble-right {
@@ -1560,12 +1348,6 @@ const getLabelStyle = (code) => {
stroke: #33ff33;
}
/* VHS 头像适配 */
.wrapped-theme-vhs .avatar-box {
background: #16213e;
border-color: #0f3460;
}
/* ========== Game Boy 主题 - 聊天气泡适配 ========== */
/* 聊天区域背景 */
@@ -1655,28 +1437,4 @@ const getLabelStyle = (code) => {
text-shadow: 0 0 3px #33ff33;
font-family: 'Courier New', monospace;
}
/* ========== VHS 主题 - 聊天气泡文字适配 ========== */
.wrapped-theme-vhs .bubble-left .wrapped-label,
.wrapped-theme-vhs .bubble-right .wrapped-label {
color: #a0a0a0 !important;
}
.wrapped-theme-vhs .bubble-left .wrapped-number,
.wrapped-theme-vhs .bubble-right .wrapped-number {
color: #eaeaea !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.5),
1px 0 rgba(255, 0, 255, 0.5);
}
.wrapped-theme-vhs .bubble-left .wrapped-body,
.wrapped-theme-vhs .bubble-right .wrapped-body {
color: #a0a0a0 !important;
}
.wrapped-theme-vhs .avatar-box svg {
stroke: #e94560;
}
</style>

View File

@@ -157,18 +157,4 @@ const originFor = (weekdayIndex, hour) => {
.wrapped-theme-dos .heatmap-legend-cell {
border-radius: 0 !important;
}
/* ========== VHS 主题 ========== */
.wrapped-theme-vhs .wrapped-label,
.wrapped-theme-vhs .wrapped-body {
color: #a0a0a0 !important;
}
.wrapped-theme-vhs .wrapped-number {
color: #e94560 !important;
text-shadow:
-1px 0 rgba(0, 255, 247, 0.5),
1px 0 rgba(255, 0, 255, 0.5);
}
</style>