Compare commits

..

3 Commits

54 changed files with 60 additions and 3789 deletions
+1 -6
View File
@@ -65,18 +65,13 @@
## 年度总结
年度总结目前只保留「现代(Modern)」风格。如果你对年度总结有更好的修改建议,欢迎到 Issue 区反馈:https://github.com/LifeArchiveProject/WeChatDataAnalysis/issues
> ⚠️ **提醒**:年度总结目前还不是最终版本,后续还会增加新总结或新内容。
也欢迎加入下方 QQ 群一起讨论。
<table>
<tr>
<td align="center"><b>Modern</b></td>
</tr>
<tr>
<td align="center"><img src="frontend/public/style1.png" alt="年度总结 Modern" width="400"/></td>
<td align="center"><img src="frontend/public/style1.png" alt="年度总结 Modern"/></td>
</tr>
</table>
+2 -485
View File
@@ -2,33 +2,6 @@
@tailwind components;
@tailwind utilities;
/* Fusion Pixel Font - 像素字体 @font-face 声明 */
/* 下载地址: https://github.com/TakWolf/fusion-pixel-font/releases */
@font-face {
font-family: 'Fusion Pixel 12';
src: url('/fonts/fusion-pixel-12px-monospaced-zh_hans.otf.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fusion Pixel 10';
src: url('/fonts/fusion-pixel-10px-monospaced-zh_hans.otf.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Fusion Pixel 8';
src: url('/fonts/fusion-pixel-8px-monospaced-zh_hans.otf.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* 自定义全局样式 - 微信配色主题 */
@layer base {
:root {
@@ -69,11 +42,6 @@
/* 统一消息圆角(聊天所有消息共用) */
--message-radius: 4px;
/* Wrapped 年度总结 - 像素字体 */
--font-pixel-12: 'Fusion Pixel 12', 'Microsoft YaHei', sans-serif;
--font-pixel-10: 'Fusion Pixel 10', 'Microsoft YaHei', sans-serif;
--font-pixel-8: 'Fusion Pixel 8', 'Microsoft YaHei', sans-serif;
}
body {
@@ -169,8 +137,7 @@
animation: noise-jitter 0.5s steps(3) infinite;
}
/* Wrapped 像素字体类 */
/* Wrapped typography: default is modern; `.wrapped-retro` enables pixel font + CRT vibe. */
/* Wrapped typography */
.wrapped-title {
font-weight: 700;
letter-spacing: 0.02em;
@@ -195,94 +162,6 @@
font-variant-numeric: tabular-nums;
}
.wrapped-retro .wrapped-title {
font-family: var(--font-pixel-12);
font-weight: normal;
letter-spacing: 0.05em;
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
.wrapped-retro .wrapped-title-en {
font-family: var(--font-pixel-12);
font-weight: normal;
letter-spacing: 0.08em;
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
.wrapped-retro .wrapped-body {
font-family: var(--font-pixel-10);
font-weight: normal;
line-height: 1.8;
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
.wrapped-retro .wrapped-label {
font-family: var(--font-pixel-8);
font-weight: normal;
letter-spacing: 0.15em;
text-transform: uppercase;
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
.wrapped-retro .wrapped-number {
font-family: var(--font-pixel-12);
font-weight: normal;
font-variant-numeric: tabular-nums;
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
/* CRT 扫描线 - 水平线条(明显可见) */
.crt-scanlines {
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 3px,
rgba(0, 0, 0, 0.15) 3px,
rgba(0, 0, 0, 0.15) 4px
);
background-size: 100% 4px;
animation: scanline-scroll 12s linear infinite;
}
/* CRT RGB 子像素 - 垂直彩色条纹 */
.crt-rgb-pixels {
background-image: repeating-linear-gradient(
to right,
rgba(255, 0, 0, 0.06) 0px,
rgba(255, 0, 0, 0.06) 1px,
rgba(0, 255, 0, 0.06) 1px,
rgba(0, 255, 0, 0.06) 2px,
rgba(0, 0, 255, 0.06) 2px,
rgba(0, 0, 255, 0.06) 3px
);
}
/* CRT 闪烁 - 亮度波动 */
.crt-flicker {
background-color: rgba(255, 255, 255, 0.01);
animation: crt-flicker 0.15s infinite alternate;
}
/* CRT 暗角 - 边缘渐暗(更强) */
.crt-vignette {
box-shadow: inset 0 0 250px 80px rgba(0, 0, 0, 0.15);
}
/* CRT 屏幕曲率效果 */
.crt-curvature {
background: radial-gradient(
ellipse at center,
transparent 0%,
transparent 40%,
rgba(0, 0, 0, 0.1) 100%
);
}
/* 输入框样式 */
.input {
@apply w-full px-4 py-3 bg-[#f7f8fa] border border-transparent rounded-xl focus:outline-none focus:ring-2 focus:ring-[#07c160] focus:bg-white focus:border-[#07c160] transition-all duration-200;
@@ -1224,24 +1103,7 @@
}
}
/* CRT 和 Wrapped 动画关键帧 */
@keyframes scanline-scroll {
0% {
background-position: 0 0;
}
100% {
background-position: 0 100vh;
}
}
@keyframes crt-flicker {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.985;
}
}
/* Wrapped 动画关键帧 */
@keyframes noise-jitter {
0% {
@@ -1273,348 +1135,3 @@
.wrapped-animate-in {
animation: wrapped-fade-in 0.6s ease-out forwards;
}
/* ============================================
Wrapped 主题系统 - Game Boy / DOS
============================================ */
/* 复古模式共享基础样式 */
.wrapped-retro {
/* 共享 CSS 变量(各主题覆盖) */
--wrapped-bg: #9bbc0f;
--wrapped-card-bg: #8bac0f;
--wrapped-text: #0f380f;
--wrapped-text-secondary: #306230;
--wrapped-accent: #0f380f;
--wrapped-border: #306230;
--wrapped-warning: #0f380f;
}
/* ============================================
Theme 1: Game Boy - 复古绿色系
============================================ */
.wrapped-theme-gameboy {
/* Game Boy 4色调色板 */
--wrapped-bg: #9bbc0f; /* 最亮绿 */
--wrapped-card-bg: #8bac0f; /* 次亮绿 */
--wrapped-text: #0f380f; /* 最深绿 */
--wrapped-text-secondary: #306230; /* 中深绿 */
--wrapped-accent: #0f380f;
--wrapped-border: #306230;
--wrapped-warning: #306230;
/* 像素化渲染 */
image-rendering: pixelated;
-webkit-font-smoothing: none;
}
/* Game Boy 像素化边框 */
.wrapped-theme-gameboy .wrapped-card-shell,
.wrapped-theme-gameboy [class*="rounded"] {
border-radius: 0 !important;
box-shadow:
inset -4px -4px 0 0 #306230,
inset 4px 4px 0 0 #9bbc0f,
0 0 0 4px #0f380f;
}
/* Game Boy 步进动画 */
.wrapped-theme-gameboy * {
animation-timing-function: steps(8) !important;
}
/* Game Boy 按钮样式 */
.wrapped-theme-gameboy button {
border-radius: 0 !important;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #9bbc0f;
transition: none !important;
}
.wrapped-theme-gameboy button:active {
box-shadow:
inset 2px 2px 0 0 #306230,
inset -2px -2px 0 0 #9bbc0f;
}
/* ============================================
Theme 2: DOS Terminal - 黑底绿字/琥珀字
============================================ */
.wrapped-theme-dos {
--wrapped-bg: #000000;
--wrapped-card-bg: #0a0a0a;
--wrapped-text: #33ff33; /* 磷光绿 */
--wrapped-text-secondary: #22aa22;
--wrapped-accent: #33ff33;
--wrapped-border: #33ff33;
--wrapped-warning: #ffaa00; /* 琥珀警告色 */
background-color: #000000 !important;
/* 使用现有 Fusion Pixel 10px 字体 */
font-family: var(--font-pixel-10), 'Courier New', monospace !important;
-webkit-font-smoothing: none;
image-rendering: pixelated;
}
/* DOS 文字发光效果 */
.wrapped-theme-dos .wrapped-title,
.wrapped-theme-dos .wrapped-body,
.wrapped-theme-dos .wrapped-label,
.wrapped-theme-dos .wrapped-number {
color: #33ff33 !important;
/* 从 4 层减少到 2 层,降低发光强度 */
text-shadow:
0 0 4px rgba(51, 255, 51, 0.8),
0 0 8px rgba(34, 170, 34, 0.4);
font-family: var(--font-pixel-10), 'Courier New', monospace !important;
-webkit-font-smoothing: none;
}
/* DOS ASCII 边框 */
.wrapped-theme-dos .wrapped-card-shell,
.wrapped-theme-dos [class*="border"] {
border: 2px solid #33ff33 !important;
border-radius: 0 !important;
box-shadow:
0 0 5px #33ff33,
inset 0 0 5px rgba(51, 255, 51, 0.1);
}
/* DOS 磷光残影效果 */
.wrapped-theme-dos * {
transition: text-shadow 0.1s ease-out !important;
}
/* DOS 扫描线(细化) */
.wrapped-theme-dos .crt-scanlines {
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 1px,
rgba(0, 0, 0, 0.15) 1px,
rgba(0, 0, 0, 0.15) 2px
) !important;
opacity: 0.5;
}
/* DOS 按钮样式 */
.wrapped-theme-dos button {
background-color: transparent !important;
border: 1px solid #33ff33 !important;
color: #33ff33 !important;
border-radius: 0 !important;
text-shadow: 0 0 5px #33ff33;
box-shadow: 0 0 5px rgba(51, 255, 51, 0.3);
}
.wrapped-theme-dos button:hover {
background-color: #33ff33 !important;
color: #000000 !important;
text-shadow: none;
}
/* ============================================
Theme 3: Windows 95/98 - Win98
============================================ */
.wrapped-theme-win98 {
/* System-like colors (approx.) */
--win98-face: #c0c0c0; /* ButtonFace */
--win98-hi: #ffffff; /* ButtonHighlight */
--win98-shadow: #808080; /* ButtonShadow */
--win98-dkshadow: #000000; /* Black */
--win98-outline: #dedede; /* Extra light line (common in Win95 clones) */
--win98-dither: repeating-conic-gradient(#bdbebd 0% 25%, #ffffff 0% 50%) 50% / 2px 2px;
--win98-title: #000080; /* ActiveCaption */
--win98-title2: #1084d0; /* Caption gradient (approx.) */
--win98-title-text: #ffffff;
--win98-title-inactive: #7b7d7b;
--win98-title-inactive-text: #e6e6e6;
/* Map to Wrapped theme variables */
--wrapped-bg: #ffffff; /* fields/content */
--wrapped-card-bg: var(--win98-face);
--wrapped-text: #000000;
--wrapped-text-secondary: #404040;
--wrapped-accent: var(--win98-title);
--wrapped-border: var(--win98-shadow);
--wrapped-warning: #800000;
font-family: "MS Sans Serif", Tahoma, "Microsoft Sans Serif", "Segoe UI", sans-serif !important;
}
/* Win98: hard edges */
.wrapped-theme-win98 [class*="rounded"] {
border-radius: 0 !important;
}
/* Win98: baseline typography (override Tailwind inline colors via !important) */
.wrapped-theme-win98 .wrapped-title {
color: var(--wrapped-text) !important;
}
.wrapped-theme-win98 .wrapped-body,
.wrapped-theme-win98 .wrapped-label {
color: var(--wrapped-text-secondary) !important;
}
.wrapped-theme-win98 .wrapped-number {
color: var(--wrapped-accent) !important;
}
/* Win98: generic raised button */
.wrapped-theme-win98 button {
background: var(--win98-face) !important;
color: #000000 !important;
border-radius: 0 !important;
text-shadow: none !important;
/* Win95-ish bevel: light (top/left) + shadow (bottom/right) + hard drop */
border-top: 1px solid var(--win98-hi) !important;
border-left: 1px solid var(--win98-hi) !important;
border-right: 1px solid var(--win98-shadow) !important;
border-bottom: 1px solid var(--win98-shadow) !important;
box-shadow: 1px 1px 0 var(--win98-dkshadow) !important;
}
.wrapped-theme-win98 button:active:not(:disabled) {
background: var(--win98-face) !important;
box-shadow: none !important;
border-top: 1px solid var(--win98-dkshadow) !important;
border-left: 1px solid var(--win98-dkshadow) !important;
border-right: 1px solid var(--win98-hi) !important;
border-bottom: 1px solid var(--win98-hi) !important;
}
.wrapped-theme-win98 button:disabled {
opacity: 0.65;
cursor: not-allowed;
}
/* Win98: dotted focus rectangle (classic) */
.wrapped-theme-win98 button:focus-visible {
outline: 1px dotted var(--win98-dkshadow);
outline-offset: -4px;
}
/* Win98 helper: checkered/dither fill for "pressed/toggled" UI (taskbar active, start menu pressed, etc.) */
.wrapped-theme-win98 .win98-dither {
background: var(--win98-dither) !important;
}
/* Win98: text-ish inputs / selects get a sunken look (avoid checkbox/radio etc.) */
.wrapped-theme-win98 textarea,
.wrapped-theme-win98 select,
.wrapped-theme-win98 input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]) {
background: var(--wrapped-bg) !important;
color: var(--wrapped-text) !important;
border-radius: 0 !important;
border: 1px solid var(--win98-shadow) !important;
box-shadow:
inset 1px 1px 0 var(--win98-dkshadow),
inset -1px -1px 0 var(--win98-hi);
}
/* Win98: window primitives (98.css-like semantics) */
.wrapped-theme-win98 .window {
background: var(--win98-face);
/* Stronger Win95-ish window frame (inspired by /win95/assets/css/windows/window.css) */
border-top: 2px solid var(--win98-hi);
border-left: 2px solid var(--win98-hi);
border-right: 1.5px solid var(--win98-shadow);
border-bottom: 1.5px solid var(--win98-shadow);
box-shadow: 1.5px 1.5px 0 var(--win98-dkshadow);
outline: 1px solid var(--win98-outline);
}
.wrapped-theme-win98 .title-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 3px 4px;
/* Win95 is typically solid; Win98 sometimes gradients. Keep solid by default. */
background: var(--win98-title);
color: var(--win98-title-text);
font-weight: 700;
user-select: none;
}
.wrapped-theme-win98 .title-bar-text {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
font-size: 11px;
line-height: 1;
}
.wrapped-theme-win98 .title-bar-icon {
width: 16px;
height: 16px;
flex: none;
image-rendering: pixelated;
}
.wrapped-theme-win98 .title-bar-text span {
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wrapped-theme-win98 .title-bar-controls {
display: inline-flex;
gap: 2px;
}
.wrapped-theme-win98 .title-bar-controls button {
width: 18px;
height: 16px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
font-weight: 700;
line-height: 1;
}
.wrapped-theme-win98 .title-bar-controls button::before {
content: "";
display: block;
}
/* Win95-ish glyphs (avoid relying on fonts for the line/square). */
.wrapped-theme-win98 .title-bar-controls button[aria-label="Minimize"]::before {
width: 6px;
height: 2px;
background: #000000;
transform: translateY(4px);
}
.wrapped-theme-win98 .title-bar-controls button[aria-label="Maximize"]::before {
width: 9px;
height: 8px;
box-sizing: border-box;
border: 1px solid #000000;
border-top-width: 2px;
background: transparent;
}
.wrapped-theme-win98 .title-bar-controls button[aria-label="Close"]::before {
content: "×";
font-size: 14px;
line-height: 1;
transform: translateY(-1px);
}
.wrapped-theme-win98 .window-body {
padding: 8px;
background: var(--win98-face);
border-top: 1px solid var(--win98-shadow);
box-shadow: inset 0 1px 0 var(--win98-hi);
}
@@ -95,7 +95,7 @@
<!-- 主内容抽奖揭晓 + 右侧年度 Top10 总消息 bar race -->
<div v-else class="w-full">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center" :class="isRetro ? 'lg:items-start' : ''">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
<!-- Left: 抽奖区 -->
<div
class="reply-buddy-rail flex flex-col items-center justify-center transition-transform duration-500 will-change-transform"
@@ -104,8 +104,7 @@
<div class="wrapped-label text-xs text-[#00000066]">最佳聊天搭子</div>
<div
class="mt-4 w-28 h-28 sm:w-32 sm:h-32 rounded-2xl border border-[#EDEDED] overflow-hidden flex items-center justify-center"
:class="isRetro ? 'bg-transparent' : 'bg-white/60'"
class="mt-4 w-28 h-28 sm:w-32 sm:h-32 rounded-2xl border border-[#EDEDED] bg-white/60 overflow-hidden flex items-center justify-center"
>
<img
v-if="shownAvatarUrl && shownAvatarOk"
@@ -115,7 +114,7 @@
@error="onShownAvatarError"
/>
<img
v-else-if="(isGameboy || isModern) && phase === 'idle'"
v-else-if="phase === 'idle'"
src="/assets/images/LuckyBlock.png"
class="w-full h-full object-contain"
alt="Lucky Block"
@@ -167,15 +166,14 @@
<!-- Right: bar race揭晓后出现 -->
<Transition name="chart-fade">
<div v-if="showChart" class="w-full" :class="isRetro ? 'lg:self-start' : ''">
<div v-if="showChart" class="w-full">
<div
class="rounded-2xl border border-[#EDEDED] bg-white/60"
:class="isRetro ? 'p-3 sm:p-4' : 'p-4 sm:p-5'"
class="rounded-2xl border border-[#EDEDED] bg-white/60 p-4 sm:p-5"
>
<div class="flex items-center justify-between gap-4">
<div>
<div class="wrapped-label text-xs text-[#00000066]">年度聊天排行总消息数</div>
<div class="wrapped-body text-sm text-[#000000e6]" :class="isRetro ? 'mt-0.5' : 'mt-1'">
<div class="wrapped-body text-sm text-[#000000e6] mt-1">
<span class="wrapped-number text-[#07C160] font-semibold">{{ raceDate }}</span>
<span class="text-[#00000055]"> · 0.1/</span>
</div>
@@ -190,21 +188,19 @@
<TransitionGroup
name="race"
tag="div"
:class="isRetro ? 'space-y-1.5' : 'space-y-2'"
class="space-y-2"
>
<div
v-for="item in raceItems"
:key="item.username"
class="race-row flex items-center"
:class="isRetro ? 'gap-3' : 'gap-3'"
class="race-row flex items-center gap-3"
>
<div class="w-6 text-right wrapped-label text-[11px] text-[#00000055]">
{{ item.rank }}
</div>
<div
class="rounded-md overflow-hidden bg-[#0000000d] flex items-center justify-center flex-shrink-0"
:class="isRetro ? 'w-6 h-6' : 'w-7 h-7'"
class="w-7 h-7 rounded-md overflow-hidden bg-[#0000000d] flex items-center justify-center flex-shrink-0"
>
<img
v-if="item.avatarUrl && avatarOk[item.username] !== false"
@@ -221,7 +217,7 @@
<div class="min-w-0 flex-1">
<div class="flex items-center justify-between gap-3">
<div class="min-w-0">
<div class="wrapped-body text-[#000000e6] truncate" :class="isRetro ? 'text-xs' : 'text-sm'" :title="item.displayName">
<div class="wrapped-body text-[#000000e6] text-sm truncate" :title="item.displayName">
{{ item.displayName }}
</div>
</div>
@@ -229,7 +225,7 @@
{{ formatInt(item.value) }}
</div>
</div>
<div class="mt-1 rounded-full bg-[#00000008] overflow-hidden" :class="isRetro ? 'h-1.5' : 'h-2'">
<div class="mt-1 h-2 rounded-full bg-[#00000008] overflow-hidden">
<div
class="race-bar h-full rounded-full bg-[#07C160]"
:style="{ width: `${item.pct}%` }"
@@ -249,18 +245,12 @@
<script setup>
import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue'
import { useWrappedTheme } from '~/composables/useWrappedTheme'
const props = defineProps({
card: { type: Object, required: true },
variant: { type: String, default: 'panel' } // 'panel' | 'slide'
})
const { theme } = useWrappedTheme()
const isGameboy = computed(() => theme.value === 'gameboy')
const isModern = computed(() => theme.value === 'off')
const isRetro = computed(() => isGameboy.value)
const nfInt = new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 0 })
const formatInt = (n) => nfInt.format(Math.round(Number(n) || 0))
@@ -1,27 +0,0 @@
<template>
<!-- CRT 滤镜叠加层 - 复古主题使用 -->
<div class="absolute inset-0 pointer-events-none select-none z-30" aria-hidden="true">
<!-- Game Boy: noise 作为最前景层统一覆盖整个画面 -->
<WrappedGameboyDither
v-if="theme === 'gameboy'"
class="opacity-[0.3]"
style="filter: contrast(1.16)"
:pattern-refresh-interval="1"
:pattern-alpha="56"
mix-blend-mode="overlay"
:pattern-size="256"
/>
<!-- 扫描线 / 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>
</div>
</template>
<script setup>
const { theme } = useWrappedTheme()
</script>
@@ -1,5 +1,5 @@
<template>
<div v-if="variant === 'panel'" class="window bg-white rounded-2xl border border-[#EDEDED] overflow-hidden">
<div v-if="variant === 'panel'" class="bg-white rounded-2xl border border-[#EDEDED] overflow-hidden">
<div class="px-6 py-5 border-b border-[#F3F3F3]">
<div class="flex items-start justify-between gap-4">
<div>
@@ -20,58 +20,24 @@
<!-- Slide 模式单张卡片占据全页面背景由外层年度总结统一控制 -->
<section v-else class="relative h-full w-full overflow-hidden">
<div :class="slideContainerClass">
<!-- Win98把整页内容包进一个窗口 -->
<div v-if="isWin98" class="window w-full flex-1 flex flex-col overflow-hidden">
<div class="title-bar">
<div class="title-bar-text">
<img class="title-bar-icon" src="/assets/images/windows-0.png" alt="" aria-hidden="true" />
<span>{{ title }}</span>
</div>
<div class="title-bar-controls" aria-hidden="true">
<button type="button" aria-label="Minimize" tabindex="-1"></button>
<button type="button" aria-label="Maximize" tabindex="-1"></button>
<button type="button" aria-label="Close" tabindex="-1"></button>
</div>
</div>
<div class="window-body flex-1 flex flex-col min-h-0">
<slot name="narrative">
<p v-if="narrative" class="wrapped-body text-sm sm:text-base whitespace-pre-wrap">
{{ narrative }}
</p>
</slot>
<div class="mt-4 flex-1 min-h-0 overflow-auto">
<div class="w-full">
<slot />
</div>
</div>
</div>
</div>
<!-- 其他主题保持原样 -->
<template v-else>
<div class="relative h-full max-w-5xl mx-auto px-6 py-10 sm:px-8 sm:py-12 flex flex-col">
<div class="flex items-start justify-between gap-4">
<div>
<h2 class="wrapped-title text-[#000000e6]" :class="slideTitleClass">{{ title }}</h2>
<div :class="slideNarrativeWrapClass">
<slot name="narrative">
<p v-if="narrative" class="mt-3 wrapped-body text-sm sm:text-base text-[#7F7F7F] max-w-2xl whitespace-pre-wrap">
{{ narrative }}
</p>
</slot>
</div>
<h2 class="wrapped-title text-2xl sm:text-3xl text-[#000000e6]">{{ title }}</h2>
<slot name="narrative">
<p v-if="narrative" class="mt-3 wrapped-body text-sm sm:text-base text-[#7F7F7F] max-w-2xl whitespace-pre-wrap">
{{ narrative }}
</p>
</slot>
</div>
<slot name="badge" />
</div>
<div class="flex-1 flex items-center" :class="slideContentWrapClass">
<div class="flex-1 flex items-center mt-6 sm:mt-8">
<div class="w-full">
<slot />
</div>
</div>
</template>
</div>
</section>
</template>
@@ -83,62 +49,4 @@ defineProps({
narrative: { type: String, default: '' },
variant: { type: String, default: 'panel' } // 'panel' | 'slide'
})
const { theme } = useWrappedTheme()
const isWin98 = computed(() => theme.value === 'win98')
const isGameboy = computed(() => theme.value === 'gameboy')
const isCompactSlide = computed(() => isGameboy.value)
const slideTitleClass = computed(() => (
isCompactSlide.value ? 'text-xl sm:text-2xl' : 'text-2xl sm:text-3xl'
))
// Keep as a computed so we can tune per-theme spacing later without touching template.
const slideNarrativeWrapClass = computed(() => '')
const slideContentWrapClass = computed(() => (
isCompactSlide.value ? 'mt-4 sm:mt-5' : 'mt-6 sm:mt-8'
))
const slideContainerClass = computed(() => (
isWin98.value
? 'relative h-full max-w-5xl mx-auto px-6 pt-2 pb-4 sm:px-8 sm:pt-3 sm:pb-6 flex flex-col'
: (isCompactSlide.value
? 'relative h-full max-w-5xl mx-auto px-6 pt-5 pb-6 sm:px-8 sm:pt-6 sm:pb-7 flex flex-col'
: 'relative h-full max-w-5xl mx-auto px-6 py-10 sm:px-8 sm:py-12 flex flex-col')
))
</script>
<style>
/* ========== Game Boy 主题 ========== */
/* 卡片背景 */
.wrapped-theme-gameboy .bg-white {
background: #9bbc0f !important;
border-color: #306230 !important;
}
/* 标题 */
.wrapped-theme-gameboy .wrapped-title {
color: #0f380f !important;
font-family: var(--font-pixel-10), 'Courier New', monospace;
}
/* 描述文字 */
.wrapped-theme-gameboy .wrapped-body {
color: #306230 !important;
}
/* 数字高亮 */
.wrapped-theme-gameboy .wrapped-number {
color: #0f380f !important;
font-family: var(--font-pixel-10), 'Courier New', monospace;
}
/* 边框 */
.wrapped-theme-gameboy .border-\[\#EDEDED\],
.wrapped-theme-gameboy .border-\[\#F3F3F3\] {
border-color: #306230 !important;
}
</style>
@@ -1,6 +1,6 @@
<template>
<!-- Shared backdrop for modern/gameboy "Wrapped" slides (keeps cover + cards visually consistent). -->
<div v-if="theme !== 'win98'" class="absolute inset-0 pointer-events-none select-none z-0" aria-hidden="true">
<!-- Shared backdrop for modern "Wrapped" slides (keeps cover + cards visually consistent). -->
<div class="absolute inset-0 pointer-events-none select-none z-0" aria-hidden="true">
<!-- Soft color blobs (brand + warm highlights) -->
<div class="absolute -top-24 -left-24 w-80 h-80 bg-[#07C160] opacity-[0.08] rounded-full blur-3xl"></div>
<div class="absolute -top-24 -right-24 w-96 h-96 bg-[#F2AA00] opacity-[0.06] rounded-full blur-3xl"></div>
@@ -11,77 +11,11 @@
class="absolute inset-0 bg-[linear-gradient(rgba(7,193,96,0.05)_1px,transparent_1px),linear-gradient(90deg,rgba(7,193,96,0.05)_1px,transparent_1px)] bg-[size:52px_52px] opacity-[0.28]"
></div>
<!-- Grain/noise: gameboy 使用动态 canvas 噪点其它主题沿用现有纹理 -->
<WrappedGameboyDither
v-if="theme === 'gameboy'"
class="opacity-[0.3]"
style="filter: contrast(1.16)"
:pattern-refresh-interval="1"
:pattern-alpha="56"
mix-blend-mode="overlay"
:pattern-size="256"
/>
<div v-else class="absolute inset-0 wrapped-noise-enhanced opacity-[0.08]"></div>
<!-- Grain/noise -->
<div class="absolute inset-0 wrapped-noise-enhanced opacity-[0.08]"></div>
<!-- Gentle vignette so typography stays readable on textured bg -->
<div class="absolute inset-x-0 top-0 h-40 bg-gradient-to-b from-white/50 to-transparent"></div>
<div class="absolute inset-x-0 bottom-0 h-44 bg-gradient-to-t from-white/40 to-transparent"></div>
</div>
<!-- Win98: classic desktop icons (purely decorative) -->
<div v-else class="absolute inset-0 pointer-events-none select-none z-0" aria-hidden="true">
<div class="win98-desktop-icons">
<div v-for="it in desktopIcons" :key="it.label" class="win98-desktop-icon">
<img class="win98-desktop-icon__img" :src="it.src" :alt="it.label" />
<div class="win98-desktop-icon__label">{{ it.label }}</div>
</div>
</div>
</div>
</template>
<script setup>
const { theme } = useWrappedTheme()
const desktopIcons = [
{ label: '我的文档', src: '/assets/images/win98-icons/folder.png' },
{ label: '图片', src: '/assets/images/win98-icons/photos.png' },
{ label: '收件箱', src: '/assets/images/win98-icons/mail.png' },
{ label: '回收站', src: '/assets/images/win98-icons/recycle.png' }
]
</script>
<style scoped>
.win98-desktop-icons {
position: absolute;
top: 84px; /* leave space for top-left controls */
left: 14px;
display: flex;
flex-direction: column;
gap: 18px;
}
.win98-desktop-icon {
width: 74px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.win98-desktop-icon__img {
width: 32px;
height: 32px;
image-rendering: pixelated;
}
.win98-desktop-icon__label {
max-width: 74px;
padding: 0 2px;
font-size: 12px;
line-height: 1.1;
color: #ffffff;
text-align: center;
text-shadow: 1px 1px 0 #000000;
word-break: break-word;
}
</style>
@@ -1,98 +0,0 @@
<template>
<canvas
ref="grainRef"
class="pointer-events-none absolute inset-0 h-full w-full"
:style="canvasStyle"
aria-hidden="true"
></canvas>
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
const props = defineProps({
patternRefreshInterval: {
type: Number,
default: 2
},
patternAlpha: {
type: Number,
default: 20
},
mixBlendMode: {
type: String,
default: 'multiply'
},
patternSize: {
type: Number,
default: 512
}
})
const grainRef = ref(null)
const canvasStyle = computed(() => `image-rendering: pixelated; mix-blend-mode: ${props.mixBlendMode};`)
let animationId = 0
let frame = 0
let noiseData
let noise32
const clamp = (value, min, max) => Math.min(max, Math.max(min, value))
const resize = () => {
const canvas = grainRef.value
if (!canvas) return
const size = Math.max(64, Math.round(props.patternSize))
canvas.width = size
canvas.height = size
}
const initImageData = (ctx) => {
const canvas = grainRef.value
if (!canvas) return
noiseData = ctx.createImageData(canvas.width, canvas.height)
noise32 = new Uint32Array(noiseData.data.buffer)
}
const drawGrain = () => {
if (!noise32) return
const alpha = clamp(Math.round(props.patternAlpha), 0, 255) << 24
for (let i = 0; i < noise32.length; i++) {
const value = (Math.random() * 255) | 0
noise32[i] = alpha | (value << 16) | (value << 8) | value
}
}
const loop = (ctx) => {
const refreshEvery = Math.max(1, Math.round(props.patternRefreshInterval))
if (frame % refreshEvery === 0) {
drawGrain()
ctx.putImageData(noiseData, 0, 0)
}
frame++
animationId = window.requestAnimationFrame(() => loop(ctx))
}
onMounted(() => {
const canvas = grainRef.value
if (!canvas) return
const ctx = canvas.getContext('2d', { alpha: true })
if (!ctx) return
resize()
initImageData(ctx)
drawGrain()
ctx.putImageData(noiseData, 0, 0)
loop(ctx)
window.addEventListener('resize', resize)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resize)
window.cancelAnimationFrame(animationId)
})
</script>
@@ -9,57 +9,7 @@
<div :class="innerClass">
<template v-if="variant === 'slide'">
<!-- Win98封面也做成一个窗口 -->
<div v-if="isWin98" class="window h-full w-full flex flex-col overflow-hidden">
<div class="title-bar">
<div class="title-bar-text">
<img class="title-bar-icon" src="/assets/images/windows-0.png" alt="" aria-hidden="true" />
<span>WECHAT WRAPPED</span>
</div>
<div class="title-bar-controls" aria-hidden="true">
<button type="button" aria-label="Minimize" tabindex="-1"></button>
<button type="button" aria-label="Maximize" tabindex="-1"></button>
<button type="button" aria-label="Close" tabindex="-1"></button>
</div>
</div>
<div class="window-body flex-1 overflow-hidden">
<div class="h-full flex flex-col justify-between">
<div class="flex items-start justify-between gap-4">
<div class="wrapped-label text-xs text-[#00000080]">
WECHAT WRAPPED
</div>
<div class="wrapped-body text-xs text-[#00000055]">
年度回望
</div>
</div>
<div class="mt-10 sm:mt-14">
<h1 class="wrapped-title text-3xl sm:text-5xl text-[#000000e6] leading-[1.05]">
{{ randomTitle.main }}
<span class="block mt-3 win98-hero-highlight">
{{ randomTitle.highlight }}
</span>
</h1>
<div class="mt-7 sm:mt-9 max-w-2xl">
<p class="wrapped-body text-base sm:text-lg text-[#00000080]">
{{ randomSubtitle }}
</p>
</div>
</div>
<div class="pb-1">
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-[#00000066]">
<!-- Intentionally left blank (avoid "feature bullet list" tone on the cover). -->
</div>
</div>
</div>
</div>
</div>
<!-- 其他主题保持原样 -->
<div v-else class="h-full flex flex-col justify-between">
<div class="h-full flex flex-col justify-between">
<div class="flex items-start justify-between gap-4">
<div class="wrapped-label text-xs text-[#00000080]">
WECHAT WRAPPED
@@ -92,79 +42,41 @@
</div>
<div
v-if="previewQuestions.length > 0 && (isGameboy || isModern)"
v-if="previewQuestions.length > 0"
class="pointer-events-none absolute bottom-0 right-0 hidden xl:flex items-end"
>
<div class="pointer-events-auto relative" :class="previewStageClass">
<div class="relative" :class="previewViewportClass">
<template v-if="isGameboy">
<BitsCardSwap
:width="previewCardWidth"
:height="previewCardHeight"
:delay="previewSwapDelay"
:card-count="previewQuestions.length"
:card-distance="previewCardDistance"
:vertical-distance="previewVerticalDistance"
:skew-amount="4"
easing="elastic"
:pause-on-hover="true"
<BitsGridMotion
:items="modernPreviewItems"
gradient-color="rgba(7, 193, 96, 0.24)"
:row-count="7"
:column-count="8"
:scroll-speed="42"
:base-offset-x="46"
>
<template
v-for="(previewItem, previewIndex) in previewQuestions"
:key="`preview-${previewItem.order}-${previewIndex}`"
v-slot:[`card-${previewIndex}`]
>
<template #item="{ item }">
<WrappedCardShell
:card-id="previewItem.order"
:title="previewItem.title"
:card-id="Number(item?.order || 0)"
:title="String(item?.title || '年度卡片')"
variant="panel"
class="h-full w-full"
class="h-full w-full preview-grid-shell"
>
<div
class="flex h-[168px] items-center justify-center rounded-xl border border-dashed px-5"
:class="previewQuestionPanelClass"
>
<p class="wrapped-body text-lg leading-relaxed text-center" :class="previewQuestionClass">
{{ previewItem.question }}
<div class="preview-grid-body">
<div class="preview-grid-summary">
{{ String(item?.summary || '年度线索') }}
</div>
<p class="preview-grid-question">
{{ String(item?.question || '这一页会揭晓你的聊天答案。') }}
</p>
<div class="preview-grid-lines" aria-hidden="true">
<span></span>
<span></span>
</div>
</div>
</WrappedCardShell>
</template>
</BitsCardSwap>
</template>
<template v-else>
<BitsGridMotion
:items="modernPreviewItems"
gradient-color="rgba(7, 193, 96, 0.24)"
:row-count="7"
:column-count="8"
:scroll-speed="42"
:base-offset-x="46"
>
<template #item="{ item }">
<WrappedCardShell
:card-id="Number(item?.order || 0)"
:title="String(item?.title || '年度卡片')"
variant="panel"
class="h-full w-full preview-grid-shell"
>
<div class="preview-grid-body">
<div class="preview-grid-summary">
{{ String(item?.summary || '年度线索') }}
</div>
<p class="preview-grid-question">
{{ String(item?.question || '这一页会揭晓你的聊天答案。') }}
</p>
<div class="preview-grid-lines" aria-hidden="true">
<span></span>
<span></span>
</div>
</div>
</WrappedCardShell>
</template>
</BitsGridMotion>
</template>
</BitsGridMotion>
</div>
</div>
</div>
@@ -362,11 +274,6 @@ const props = defineProps({
cardManifests: { type: Array, default: () => [] }
})
const { theme } = useWrappedTheme()
const isWin98 = computed(() => theme.value === 'win98')
const isGameboy = computed(() => theme.value === 'gameboy')
const isModern = computed(() => theme.value === 'off')
const previewQuestions = computed(() => {
const manifests = Array.isArray(props.cardManifests) ? props.cardManifests : []
if (!manifests.length) {
@@ -392,10 +299,6 @@ const previewQuestions = computed(() => {
})
})
const previewSwapDelay = 4200
const previewCardWidth = 420
const previewCardHeight = 280
const modernPreviewItems = computed(() => {
if (!previewQuestions.value.length) return []
return previewQuestions.value.map((item) => ({
@@ -407,15 +310,11 @@ const modernPreviewItems = computed(() => {
})
const previewStageClass = computed(() => (
isGameboy.value
? 'w-[500px] h-[360px] translate-x-24 -translate-y-8'
: 'w-[620px] h-[420px] translate-x-32 -translate-y-10'
'w-[620px] h-[420px] translate-x-32 -translate-y-10'
))
const previewViewportClass = computed(() => (
isGameboy.value
? 'h-[340px] w-[460px]'
: 'h-[390px] w-[580px]'
'h-[390px] w-[580px]'
))
const previewCardDistance = computed(() => {
@@ -428,16 +327,6 @@ const previewVerticalDistance = computed(() => {
return total >= 9 ? 10 : total >= 7 ? 11 : total >= 5 ? 14 : 18
})
const previewQuestionClass = computed(() => {
if (isWin98.value) return 'text-[#111111]'
return 'text-[#1F2937]'
})
const previewQuestionPanelClass = computed(() => {
if (isWin98.value) return 'border-[#B7B7B7] bg-[#FFFFFF]'
return 'border-[#07C160]/30 bg-[#F7FFFB]'
})
const yearText = computed(() => `${props.year}`)
const rootClass = computed(() => {
@@ -449,20 +338,11 @@ const rootClass = computed(() => {
const innerClass = computed(() => {
if (props.variant !== 'slide') return 'relative px-6 py-7 sm:px-8 sm:py-9'
if (isWin98.value) return 'relative h-full max-w-5xl mx-auto px-6 pt-2 pb-4 sm:px-8 sm:pt-3 sm:pb-6'
return 'relative h-full max-w-5xl mx-auto px-6 py-10 sm:px-8 sm:py-12'
})
</script>
<style scoped>
/* Win98:封面标题的高亮句做成“选中/标题栏”感觉 */
.win98-hero-highlight {
display: inline-block;
padding: 2px 8px;
background: #000080;
color: #ffffff;
}
.preview-grid-shell {
border-radius: 12px;
box-shadow: 0 10px 24px rgba(7, 193, 96, 0.14);
@@ -1,17 +0,0 @@
<template>
<component :is="themeSwitcherComponent" />
</template>
<script setup>
const { theme } = useWrappedTheme()
// 根据当前主题动态选择对应的切换器组件
const themeSwitcherComponent = computed(() => {
const map = {
off: resolveComponent('WrappedThemeSwitcherModern'),
gameboy: resolveComponent('WrappedThemeSwitcherGameboy'),
win98: resolveComponent('WrappedThemeSwitcherWin98')
}
return map[theme.value] || map.off
})
</script>
@@ -1,97 +0,0 @@
<template>
<div class="gameboy-menu select-none">
<!-- 像素风格菜单框 -->
<div class="gameboy-menu-box">
<div class="gameboy-menu-title">SELECT THEME</div>
<div class="gameboy-menu-items">
<button
v-for="t in themes"
:key="t.value"
class="gameboy-menu-item"
:class="{ 'is-active': theme === t.value }"
@click="selectTheme(t.value)"
>
<span class="gameboy-cursor">{{ theme === t.value ? '▶' : ' ' }}</span>
<span class="gameboy-label">{{ t.label }}</span>
</button>
</div>
</div>
</div>
</template>
<script setup>
const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'MODERN' },
{ value: 'gameboy', label: 'GAME BOY' },
{ value: 'win98', label: 'WIN98' }
]
const selectTheme = (value) => {
setTheme(value)
}
</script>
<style scoped>
.gameboy-menu {
font-family: 'Press Start 2P', 'Courier New', monospace;
font-size: 8px;
line-height: 1.5;
}
.gameboy-menu-box {
background: #0f380f;
border: 3px solid #306230;
padding: 8px;
box-shadow:
inset 2px 2px 0 #9bbc0f,
inset -2px -2px 0 #0f380f;
}
.gameboy-menu-title {
color: #9bbc0f;
text-align: center;
margin-bottom: 8px;
padding-bottom: 4px;
border-bottom: 2px dashed #306230;
}
.gameboy-menu-items {
display: flex;
flex-direction: column;
gap: 2px;
}
.gameboy-menu-item {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 6px;
color: #9bbc0f;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
transition: background-color 0.1s;
font-family: inherit;
font-size: inherit;
}
.gameboy-menu-item:hover {
background: #306230;
}
.gameboy-menu-item.is-active {
color: #8bac0f;
}
.gameboy-cursor {
width: 10px;
display: inline-block;
}
.gameboy-label {
letter-spacing: 1px;
}
</style>
@@ -1,30 +0,0 @@
<template>
<div class="flex items-center gap-2">
<span class="text-xs text-[#00000099]">Theme</span>
<div class="inline-flex rounded-lg border border-[#EDEDED] overflow-hidden">
<button
v-for="t in themes"
:key="t.value"
class="px-3 py-1.5 text-xs wrapped-label transition-colors"
:class="[
theme === t.value
? 'bg-[#07C160] text-white'
: 'bg-white text-[#333] hover:bg-[#F5F5F5]'
]"
@click="setTheme(t.value)"
>
{{ t.label }}
</button>
</div>
</div>
</template>
<script setup>
const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'Modern' },
{ value: 'gameboy', label: 'Game Boy' },
{ value: 'win98', label: 'Win98' }
]
</script>
@@ -1,73 +0,0 @@
<template>
<div class="win98-switcher select-none">
<span class="win98-switcher__label">Theme</span>
<div class="win98-switcher__group" role="group" aria-label="Theme switcher">
<button
v-for="t in themes"
:key="t.value"
type="button"
class="win98-switcher__btn"
:class="{ 'is-active': theme === t.value }"
@click="setTheme(t.value)"
>
{{ t.label }}
</button>
</div>
</div>
</template>
<script setup>
const { theme, setTheme } = useWrappedTheme()
const themes = [
{ value: 'off', label: 'Modern' },
{ value: 'gameboy', label: 'Game Boy' },
{ value: 'win98', label: 'Win98' }
]
</script>
<style scoped>
.win98-switcher {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 11px;
}
.win98-switcher__label {
color: rgba(0, 0, 0, 0.75);
}
/* Bevel group container */
.win98-switcher__group {
display: inline-flex;
align-items: stretch;
padding: 2px;
background: #c0c0c0;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.win98-switcher__btn {
padding: 4px 10px;
background: #c0c0c0;
color: #000000;
border: 1px solid transparent;
cursor: pointer;
font: inherit;
line-height: 1;
}
.win98-switcher__btn:hover {
filter: brightness(1.03);
}
.win98-switcher__btn.is-active {
background: #000080 !important;
color: #ffffff !important;
border-color: #000080 !important;
}
</style>
@@ -1,169 +0,0 @@
<template>
<div class="win98-taskbar" @wheel.stop.prevent>
<button
type="button"
class="win98-start"
aria-label="Start"
:aria-pressed="startPressed ? 'true' : 'false'"
@mousedown="startPressed = true"
@mouseup="startPressed = false"
@mouseleave="startPressed = false"
>
<img class="win98-start__icon" src="/assets/images/windows-0.png" alt="" aria-hidden="true" />
<span class="win98-start__text">Start</span>
</button>
<div class="win98-taskbar__divider" aria-hidden="true"></div>
<button
type="button"
class="win98-task"
:title="title"
tabindex="-1"
aria-label="Active window"
>
{{ title }}
</button>
<div class="win98-taskbar__spacer" aria-hidden="true"></div>
<div class="win98-tray" aria-label="System tray">
<div class="win98-tray__clock" :title="timeText">
{{ timeText }}
</div>
</div>
</div>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'
defineProps({
title: { type: String, default: 'WeChat Wrapped' }
})
const startPressed = ref(false)
const timeText = ref('--:--')
let timer = null
const formatWin98Time = (d) => {
try {
// Win98 screenshot style: 12-hour + AM/PM
return new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit' }).format(d)
} catch {
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return `${hh}:${mm}`
}
}
const updateClock = () => { timeText.value = formatWin98Time(new Date()) }
onMounted(() => {
updateClock()
timer = setInterval(updateClock, 30_000)
})
onBeforeUnmount(() => {
if (timer) clearInterval(timer)
timer = null
})
</script>
<style scoped>
.win98-taskbar {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 40px;
display: flex;
align-items: center;
gap: 6px;
padding: 4px;
background: #c0c0c0;
border-top: 2px solid #ffffff;
z-index: 40;
}
.win98-start {
display: inline-flex;
align-items: center;
gap: 6px;
height: 30px;
padding: 0 10px 0 8px;
font-weight: 700;
}
.win98-start__icon {
width: 16px;
height: 16px;
image-rendering: pixelated;
}
.win98-start__text {
line-height: 1;
}
.win98-taskbar__divider {
width: 2px;
height: 28px;
background: #808080;
box-shadow: 1px 0 0 #ffffff;
}
.win98-task {
height: 30px;
min-width: 160px;
max-width: 56vw;
padding: 0 10px;
font-weight: 400;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.win98-task {
/* Active window task button: depressed + dither fill (Win95-ish) */
background: var(--win98-dither) !important;
box-shadow: none !important;
border-top: 1px solid var(--win98-dkshadow) !important;
border-left: 1px solid var(--win98-dkshadow) !important;
border-right: 1px solid var(--win98-hi) !important;
border-bottom: 1px solid var(--win98-hi) !important;
}
.win98-start[aria-pressed="true"] {
/* Start button pressed: depressed + dither */
background: var(--win98-dither) !important;
box-shadow: none !important;
border-top: 1px solid var(--win98-dkshadow) !important;
border-left: 1px solid var(--win98-dkshadow) !important;
border-right: 1px solid var(--win98-hi) !important;
border-bottom: 1px solid var(--win98-hi) !important;
}
.win98-taskbar__spacer {
flex: 1;
}
.win98-tray {
display: inline-flex;
align-items: center;
height: 30px;
padding: 0 8px;
background: #c0c0c0;
border-top: 1px solid var(--win98-shadow);
border-left: 1px solid var(--win98-shadow);
border-right: 1px solid var(--win98-hi);
border-bottom: 1px solid var(--win98-hi);
}
.win98-tray__clock {
font-size: 11px;
color: #000000;
line-height: 1;
white-space: nowrap;
}
</style>
@@ -1,45 +1,6 @@
<template>
<div class="year-selector" :class="selectorClass">
<!-- Game Boy 风格 -->
<div v-if="theme === 'gameboy'" class="year-gameboy">
<div class="gameboy-year-box">
<button
class="gameboy-arrow"
:disabled="!canGoPrev"
@click="prevYear"
aria-label="Previous year"
></button>
<span class="gameboy-year-value">{{ modelValue }}</span>
<button
class="gameboy-arrow"
:disabled="!canGoNext"
@click="nextYear"
aria-label="Next year"
></button>
</div>
</div>
<!-- Win98 风格 -->
<div v-else-if="theme === 'win98'" class="year-win98">
<div class="win98-year-box">
<button
class="win98-arrow"
:disabled="!canGoPrev"
@click="prevYear"
aria-label="Previous year"
></button>
<span class="win98-year-value">{{ modelValue }}</span>
<button
class="win98-arrow"
:disabled="!canGoNext"
@click="nextYear"
aria-label="Next year"
></button>
</div>
</div>
<!-- Modern 风格下拉菜单默认 -->
<div v-else class="year-modern">
<div class="year-selector">
<div 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"
@@ -81,8 +42,6 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue'])
const { theme } = useWrappedTheme()
const currentIndex = computed(() => props.years.indexOf(props.modelValue))
const canGoPrev = computed(() => currentIndex.value > 0)
const canGoNext = computed(() => currentIndex.value < props.years.length - 1)
@@ -106,10 +65,6 @@ const onSelectChange = (e) => {
}
}
const selectorClass = computed(() => {
return `year-selector-${theme.value}`
})
// 全局左右键切换年份(所有主题)
const handleKeydown = (e) => {
if (props.years.length <= 1) return
@@ -144,102 +99,4 @@ onBeforeUnmount(() => {
display: flex;
align-items: center;
}
/* ========== Game Boy 风格 ========== */
.year-gameboy {
font-family: 'Press Start 2P', 'Courier New', monospace;
}
.gameboy-year-box {
display: flex;
align-items: center;
gap: 4px;
background: #0f380f;
border: 3px solid #306230;
padding: 6px 8px;
box-shadow:
inset 2px 2px 0 #9bbc0f,
inset -2px -2px 0 #0f380f;
}
.gameboy-arrow {
background: #306230;
border: none;
color: #9bbc0f;
font-size: 8px;
padding: 4px 6px;
cursor: pointer;
transition: background 0.1s;
}
.gameboy-arrow:hover:not(:disabled) {
background: #8bac0f;
color: #0f380f;
}
.gameboy-arrow:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.gameboy-year-value {
color: #9bbc0f;
font-size: 10px;
min-width: 40px;
text-align: center;
letter-spacing: 2px;
}
/* ========== Win98 风格 ========== */
.year-win98 {
font-family: "MS Sans Serif", Tahoma, "Microsoft Sans Serif", "Segoe UI", sans-serif;
font-size: 11px;
}
.win98-year-box {
display: inline-flex;
align-items: center;
background: #c0c0c0;
padding: 2px;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.win98-year-value {
min-width: 62px;
text-align: center;
color: #000000;
padding: 2px 8px;
background: #ffffff;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #000000,
inset -1px -1px 0 #ffffff;
}
.win98-arrow {
width: 24px;
height: 22px;
background: #c0c0c0;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
cursor: pointer;
font: inherit;
line-height: 1;
}
.win98-arrow:active:not(:disabled) {
box-shadow:
inset 1px 1px 0 #000000,
inset -1px -1px 0 #ffffff;
}
.win98-arrow:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
@@ -309,126 +309,4 @@ watch(
opacity: 1;
transform: translateX(0);
}
/* ========== Game Boy 主题 ========== */
.wrapped-theme-gameboy .wrapped-chat-replay {
--wr-chat-frame-bg: #9bbc0f;
--wr-chat-top-bg: #8bac0f;
--wr-chat-chat-bg: #9bbc0f;
--wr-chat-border: #306230;
--wr-chat-bubble-bg: #8bac0f;
--wr-chat-bubble-tail: #8bac0f;
--wr-chat-bubble-text: #0f380f;
--wr-chat-typing-bg: #c5d870;
--wr-chat-typing-dot: #0f380f;
border-width: 4px;
border-radius: 0;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #c5d870;
}
.wrapped-theme-gameboy .wrapped-chat-replay__top {
border-bottom-width: 3px;
}
.wrapped-theme-gameboy .wrapped-chat-replay__typing,
.wrapped-theme-gameboy .wrapped-chat-replay__bubble {
border-radius: 0;
border: 3px solid #306230;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #9bbc0f;
}
.wrapped-theme-gameboy .wrapped-chat-replay__top .wrapped-label,
.wrapped-theme-gameboy .wrapped-chat-replay__top .wrapped-body {
color: #0f380f !important;
}
.wrapped-theme-gameboy .wrapped-chat-replay__bubble-text {
font-family: var(--font-pixel-10), 'Courier New', monospace;
}
/* ========== DOS 主题 ========== */
.wrapped-theme-dos .wrapped-chat-replay {
--wr-chat-frame-bg: #0a0a0a;
--wr-chat-top-bg: #0a0a0a;
--wr-chat-chat-bg: #0a0a0a;
--wr-chat-border: #33ff33;
--wr-chat-bubble-bg: #0a0a0a;
--wr-chat-bubble-tail: #0a0a0a;
--wr-chat-bubble-text: #33ff33;
--wr-chat-typing-bg: #0a0a0a;
--wr-chat-typing-dot: #33ff33;
box-shadow: 0 0 10px rgba(51, 255, 51, 0.2);
}
.wrapped-theme-dos .wrapped-chat-replay__typing,
.wrapped-theme-dos .wrapped-chat-replay__bubble {
border: 1px solid #33ff33;
box-shadow: 0 0 6px rgba(51, 255, 51, 0.18);
}
.wrapped-theme-dos .wrapped-chat-replay__top .wrapped-label,
.wrapped-theme-dos .wrapped-chat-replay__top .wrapped-body {
color: #33ff33 !important;
text-shadow: 0 0 3px rgba(51, 255, 51, 0.6);
}
.wrapped-theme-dos .wrapped-chat-replay__bubble-text {
font-family: 'Courier New', monospace;
text-shadow: 0 0 3px rgba(51, 255, 51, 0.6);
}
/* ========== Win98 主题 ========== */
.wrapped-theme-win98 .wrapped-chat-replay {
--wr-chat-frame-bg: #c0c0c0;
--wr-chat-top-bg: linear-gradient(90deg, #000080, #1084d0);
--wr-chat-chat-bg: #ffffff;
--wr-chat-border: #808080;
--wr-chat-bubble-bg: #ffffff;
--wr-chat-bubble-tail: #ffffff;
--wr-chat-bubble-text: #000000;
--wr-chat-typing-bg: #ffffff;
--wr-chat-typing-dot: #000000;
border-radius: 0;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .wrapped-chat-replay__top {
border-bottom: 1px solid #808080;
}
.wrapped-theme-win98 .wrapped-chat-replay__top .wrapped-label,
.wrapped-theme-win98 .wrapped-chat-replay__top .wrapped-body {
/* Title-bar text should be white on the Win98 blue gradient. */
color: #ffffff !important;
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.35);
}
.wrapped-theme-win98 .wrapped-chat-replay__avatar-fallback {
color: rgba(255, 255, 255, 0.9);
}
.wrapped-theme-win98 .wrapped-chat-replay__typing,
.wrapped-theme-win98 .wrapped-chat-replay__bubble {
border-radius: 0;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
</style>
@@ -268,160 +268,4 @@ const labels = computed(() => {
.overview-progress-fill {
@apply h-full rounded-full bg-[#07C160];
}
/* ========== Game Boy 主题 ========== */
.wrapped-theme-gameboy .overview-card {
background: #8bac0f !important;
border: 4px solid #306230 !important;
border-radius: 0 !important;
backdrop-filter: none;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #c5d870;
}
.wrapped-theme-gameboy .overview-progress-bg {
background: #306230 !important;
border-radius: 0 !important;
}
.wrapped-theme-gameboy .overview-progress-fill {
background: #0f380f !important;
border-radius: 0 !important;
}
.wrapped-theme-gameboy .overview-grid-line {
stroke: #306230;
stroke-opacity: 0.4;
}
.wrapped-theme-gameboy .overview-axis-line {
stroke: #306230;
stroke-opacity: 0.5;
}
.wrapped-theme-gameboy .overview-data-polygon {
fill: rgba(15, 56, 15, 0.3);
stroke: #0f380f;
}
.wrapped-theme-gameboy .overview-data-node {
fill: #0f380f;
stroke: #9bbc0f;
}
.wrapped-theme-gameboy .overview-label {
fill: #0f380f;
}
.wrapped-theme-gameboy .wrapped-label,
.wrapped-theme-gameboy .wrapped-body {
color: #306230 !important;
}
.wrapped-theme-gameboy .wrapped-number {
color: #0f380f !important;
}
/* ========== DOS 主题 ========== */
.wrapped-theme-dos .overview-card {
background: #0a0a0a !important;
border: 1px solid #33ff33 !important;
box-shadow: 0 0 10px rgba(51, 255, 51, 0.2);
backdrop-filter: none;
}
.wrapped-theme-dos .overview-grid-line {
stroke: #33ff33;
stroke-opacity: 0.2;
}
.wrapped-theme-dos .overview-axis-line {
stroke: #33ff33;
stroke-opacity: 0.3;
}
.wrapped-theme-dos .overview-data-polygon {
fill: rgba(51, 255, 51, 0.15);
stroke: #33ff33;
}
.wrapped-theme-dos .overview-data-node {
fill: #33ff33;
stroke: #0a0a0a;
}
.wrapped-theme-dos .overview-label {
fill: #33ff33;
}
.wrapped-theme-dos .overview-progress-bg {
background: #1a1a1a !important;
border: 1px solid #33ff33;
}
.wrapped-theme-dos .overview-progress-fill {
background: #33ff33 !important;
box-shadow: 0 0 5px #33ff33;
}
.wrapped-theme-dos .wrapped-label,
.wrapped-theme-dos .wrapped-body {
color: #22aa22 !important;
text-shadow: 0 0 3px #22aa22;
}
.wrapped-theme-dos .wrapped-number {
color: #33ff33 !important;
text-shadow: 0 0 5px #33ff33;
}
/* ========== Win98 主题 ========== */
.wrapped-theme-win98 .overview-card {
background: #c0c0c0 !important;
border: 1px solid #808080 !important;
border-radius: 0 !important;
backdrop-filter: none;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .overview-progress-bg {
background: #ffffff !important;
border-radius: 0 !important;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #000000,
inset -1px -1px 0 #ffffff;
}
.wrapped-theme-win98 .overview-progress-fill {
background: #000080 !important;
border-radius: 0 !important;
}
.wrapped-theme-win98 .overview-grid-line {
stroke: rgba(0, 0, 0, 0.22);
}
.wrapped-theme-win98 .overview-axis-line {
stroke: rgba(0, 0, 0, 0.35);
}
.wrapped-theme-win98 .overview-data-polygon {
fill: rgba(0, 0, 128, 0.16);
stroke: #000080;
}
.wrapped-theme-win98 .overview-data-node {
fill: #000080;
stroke: #ffffff;
}
.wrapped-theme-win98 .overview-label {
fill: rgba(0, 0, 0, 0.8);
}
</style>
@@ -791,929 +791,4 @@ const getLabelStyle = (code) => {
@apply mt-2 text-center text-[8px] text-[#00000025] tracking-[0.15em] uppercase;
}
/* ========== 复古模式 - 8-bit 像素风格键盘 ========== */
/* 全局像素化渲染 */
.wrapped-retro .keyboard-outer,
.wrapped-retro .keyboard-outer * {
image-rendering: pixelated;
-webkit-font-smoothing: none;
-moz-osx-font-smoothing: unset;
}
/* 键盘外框 - 粗像素边框,Game Boy 风格 */
.wrapped-retro .keyboard-outer {
border-radius: 0;
background: #8b956d;
border: none;
padding: 4px;
/* 多层像素边框效果 */
box-shadow:
0 0 0 4px #2d3320,
0 0 0 8px #5a6448,
0 0 0 10px #2d3320,
inset 0 0 0 2px #a5b38a;
}
.wrapped-retro .keyboard-inner {
border-radius: 0;
background: #9aa582;
border: none;
padding: 6px;
/* 内凹像素边框 */
box-shadow:
inset 4px 4px 0 #6b7a54,
inset -4px -4px 0 #c5d4a8;
}
/* 顶部装饰点 - 大像素方块 + 闪烁动画 */
.wrapped-retro .dot {
border-radius: 0;
width: 10px;
height: 10px;
box-shadow:
2px 2px 0 rgba(0,0,0,0.5),
inset -2px -2px 0 rgba(0,0,0,0.3);
}
.wrapped-retro .dot-red {
background: #e43b44;
animation: pixel-blink 1s steps(2) infinite;
}
.wrapped-retro .dot-yellow {
background: #f7d51d;
animation: pixel-blink 1.5s steps(2) infinite 0.3s;
}
.wrapped-retro .dot-green {
background: #63c64d;
animation: pixel-blink 2s steps(2) infinite 0.6s;
}
@keyframes pixel-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
/* 统计文字 - 像素字体 + 阴影 */
.wrapped-retro .keyboard-stats {
font-family: var(--font-pixel-10), 'Courier New', monospace;
font-size: 10px;
color: #2d3320;
letter-spacing: 1px;
text-shadow: 1px 1px 0 #c5d4a8;
}
.wrapped-retro .keyboard-hint {
font-family: var(--font-pixel-10), 'Courier New', monospace;
font-size: 9px;
color: #4a5a38;
text-shadow: 1px 1px 0 #c5d4a8;
}
/* 键盘主体 - 像素网格背景 */
.wrapped-retro .keyboard-body {
border-radius: 0;
background:
repeating-linear-gradient(
0deg,
#7a8a62 0px, #7a8a62 2px,
#8b9b72 2px, #8b9b72 4px
);
padding: 4px;
box-shadow:
inset 3px 3px 0 #5a6a48,
inset -3px -3px 0 #a5b592;
}
/* 键帽基础 - 粗像素边框 */
.wrapped-retro .kb-key::before {
border-radius: 0;
background: #3d4a2d;
box-shadow: none;
}
.wrapped-retro .kb-key-top {
border-radius: 0;
border: none;
background: #c5d4a8 !important;
/* 3D 像素凸起效果 - 多层 box-shadow */
box-shadow:
inset -3px -3px 0 #7a8a62,
inset 3px 3px 0 #e8f4d8,
inset -1px -1px 0 #5a6a48,
inset 1px 1px 0 #f0fce0 !important;
}
/* 键帽标签 - 粗像素字体 */
.wrapped-retro .kb-label {
font-family: var(--font-pixel-10), 'Courier New', monospace;
font-size: 8px;
font-weight: bold;
color: #2d3320;
text-shadow: 1px 1px 0 #e8f4d8;
filter: none !important;
letter-spacing: 0;
}
@media (min-width: 640px) {
.wrapped-retro .kb-label {
font-size: 10px;
}
}
.wrapped-retro .kb-label-sm {
font-size: 6px !important;
}
@media (min-width: 640px) {
.wrapped-retro .kb-label-sm {
font-size: 7px !important;
}
}
.wrapped-retro .kb-sub {
font-family: var(--font-pixel-10), 'Courier New', monospace;
font-size: 5px;
color: #5a6a48;
text-shadow: none;
filter: none !important;
}
@media (min-width: 640px) {
.wrapped-retro .kb-sub {
font-size: 6px;
}
}
/* 空格键凹槽 - 像素凹陷 */
.wrapped-retro .kb-space-bar {
border-radius: 0;
background: #5a6a48;
box-shadow:
inset 2px 2px 0 #3d4a2d,
inset -1px -1px 0 #7a8a62;
height: 3px;
}
/* 品牌文字 */
.wrapped-retro .keyboard-brand {
font-family: var(--font-pixel-10), 'Courier New', monospace;
color: #5a6a48;
letter-spacing: 2px;
text-shadow: 1px 1px 0 #c5d4a8;
}
/* ========== 复古模式 - 像素化磨损等级 ========== */
/* Level 1-2: 轻微变色 + 中心模糊污渍 */
.wrapped-retro .kb-level-1 .kb-key-top,
.wrapped-retro .kb-level-2 .kb-key-top {
background: #b5c498 !important;
}
.wrapped-retro .kb-level-1 .kb-key-top::after,
.wrapped-retro .kb-level-2 .kb-key-top::after {
content: '';
position: absolute;
inset: 25%;
background: radial-gradient(ellipse at center, #8b9b72 0%, transparent 70%);
opacity: 0.4;
pointer-events: none;
}
.wrapped-retro .kb-level-2 .kb-key-top::after {
inset: 20%;
opacity: 0.5;
}
/* Level 3-4: 更深的模糊污渍 */
.wrapped-retro .kb-level-3 .kb-key-top,
.wrapped-retro .kb-level-4 .kb-key-top {
background: #a5b488 !important;
}
.wrapped-retro .kb-level-3 .kb-key-top::after,
.wrapped-retro .kb-level-4 .kb-key-top::after {
content: '';
position: absolute;
inset: 15%;
background: radial-gradient(ellipse at center, #6b7a54 0%, #7a8a62 40%, transparent 70%);
opacity: 0.5;
pointer-events: none;
}
.wrapped-retro .kb-level-4 .kb-key-top::after {
inset: 10%;
background: radial-gradient(ellipse at center, #5a6a48 0%, #6b7a54 30%, #7a8a62 50%, transparent 75%);
opacity: 0.6;
}
/* Level 5-6: 凹陷效果 + 磨损渐变 + 裂纹线 */
.wrapped-retro .kb-level-5 .kb-key-top,
.wrapped-retro .kb-level-6 .kb-key-top {
background: #95a478 !important;
/* 反转阴影 = 凹陷效果 */
box-shadow:
inset 3px 3px 0 #6b7a54,
inset -3px -3px 0 #b5c498,
inset 1px 1px 0 #5a6a48 !important;
}
.wrapped-retro .kb-level-5 .kb-key-top::before,
.wrapped-retro .kb-level-6 .kb-key-top::before {
content: '';
position: absolute;
top: 0;
right: 2px;
width: 2px;
height: 40%;
background: linear-gradient(to bottom, #3d4a2d, transparent);
z-index: 2;
}
.wrapped-retro .kb-level-5 .kb-key-top::after,
.wrapped-retro .kb-level-6 .kb-key-top::after {
content: '';
position: absolute;
inset: 10%;
background: radial-gradient(ellipse at center, #5a6a48 0%, #6b7a54 30%, transparent 65%);
opacity: 0.6;
pointer-events: none;
}
.wrapped-retro .kb-level-6 .kb-key-top::before {
height: 55%;
}
.wrapped-retro .kb-level-6 .kb-key-top::after {
opacity: 0.7;
}
/* Level 7-8: 严重磨损 + 裂纹 */
.wrapped-retro .kb-level-7 .kb-key-top,
.wrapped-retro .kb-level-8 .kb-key-top {
background: #859468 !important;
box-shadow:
inset 3px 3px 0 #5a6a48,
inset -3px -3px 0 #a5b488,
inset 2px 2px 0 #4a5a38 !important;
}
.wrapped-retro .kb-level-7 .kb-key-top::before {
content: '';
position: absolute;
inset: 0;
/* 对角裂纹 */
background:
linear-gradient(135deg,
transparent 0%, transparent 45%,
#3d4a2d 45%, #3d4a2d 48%,
transparent 48%, transparent 100%
);
pointer-events: none;
z-index: 2;
}
.wrapped-retro .kb-level-7 .kb-key-top::after {
content: '';
position: absolute;
inset: 5%;
background: radial-gradient(ellipse at center, #4a5a38 0%, #5a6a48 25%, transparent 60%);
opacity: 0.7;
pointer-events: none;
}
/* Level 8: 缺角 + 交叉裂纹 + 深度磨损 */
.wrapped-retro .kb-broken-tl .kb-key-top {
clip-path: polygon(6px 0%, 100% 0%, 100% 100%, 0% 100%, 0% 6px);
}
.wrapped-retro .kb-broken-tr .kb-key-top {
clip-path: polygon(0% 0%, calc(100% - 6px) 0%, 100% 6px, 100% 100%, 0% 100%);
}
.wrapped-retro .kb-broken-bl .kb-key-top {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 6px 100%, 0% calc(100% - 6px));
}
.wrapped-retro .kb-broken-br .kb-key-top {
clip-path: polygon(0% 0%, 100% 0%, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0% 100%);
}
.wrapped-retro .kb-level-8 .kb-key-top::before {
content: '';
position: absolute;
inset: 0;
/* 交叉裂纹 */
background:
linear-gradient(135deg, transparent 46%, #3d4a2d 46%, #3d4a2d 50%, transparent 50%),
linear-gradient(45deg, transparent 46%, #3d4a2d 46%, #3d4a2d 50%, transparent 50%);
pointer-events: none;
z-index: 2;
}
.wrapped-retro .kb-level-8 .kb-key-top::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, #3d4a2d 0%, #4a5a38 20%, transparent 55%);
opacity: 0.75;
pointer-events: none;
}
/* Level 9: 严重损坏 - 大块缺失 + 深色磨损 */
.wrapped-retro .kb-level-9 .kb-key-top {
background: #758458 !important;
box-shadow:
inset 3px 3px 0 #4a5a38,
inset -3px -3px 0 #95a478 !important;
}
.wrapped-retro .kb-shattered-tl .kb-key-top {
clip-path: polygon(10px 0%, 100% 0%, 100% 100%, 0% 100%, 0% 10px);
}
.wrapped-retro .kb-shattered-tr .kb-key-top {
clip-path: polygon(0% 0%, calc(100% - 10px) 0%, 100% 10px, 100% 100%, 0% 100%);
}
.wrapped-retro .kb-shattered-bl .kb-key-top {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 10px 100%, 0% calc(100% - 10px));
}
.wrapped-retro .kb-shattered-br .kb-key-top {
clip-path: polygon(0% 0%, 100% 0%, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0% 100%);
}
.wrapped-retro .kb-level-9 .kb-key-top::before {
content: '';
position: absolute;
inset: 0;
/* 多条裂纹 */
background:
linear-gradient(135deg, transparent 30%, #2d3320 30%, #2d3320 33%, transparent 33%),
linear-gradient(135deg, transparent 60%, #2d3320 60%, #2d3320 63%, transparent 63%),
linear-gradient(45deg, transparent 45%, #2d3320 45%, #2d3320 48%, transparent 48%);
pointer-events: none;
z-index: 2;
}
.wrapped-retro .kb-level-9 .kb-key-top::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, #2d3320 0%, #3d4a2d 15%, #4a5a38 30%, transparent 55%);
opacity: 0.8;
pointer-events: none;
}
/* Level 10: 完全报废 - 键帽脱落露出轴体 */
.wrapped-retro .kb-level-10 .kb-key-top {
opacity: 0 !important;
}
.wrapped-retro .kb-level-10::before {
background: #2d3320 !important;
border-radius: 0;
box-shadow:
inset 2px 2px 0 #1a1f14,
inset -2px -2px 0 #4a5a38;
}
.wrapped-retro .kb-level-10::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
/* 简化轴体 - 纯色方块 + 凹陷效果 */
background: #6b7a54;
border-radius: 0;
box-shadow:
inset 2px 2px 0 #8b956d,
inset -2px -2px 0 #4a5a38;
z-index: 1;
}
@media (min-width: 640px) {
.wrapped-retro .kb-level-10::after {
width: 10px;
height: 10px;
}
}
/* 复古模式下移除性能优化的 will-change */
.wrapped-retro .kb-level-8 .kb-key-top,
.wrapped-retro .kb-level-9 .kb-key-top {
will-change: auto;
}
/* 复古模式 - 扫描线效果(可选,增强 CRT 感) */
.wrapped-retro .keyboard-outer::after {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent 0px,
transparent 2px,
rgba(0, 0, 0, 0.03) 2px,
rgba(0, 0, 0, 0.03) 4px
);
pointer-events: none;
z-index: 100;
}
/* ========== Game Boy 主题 - DMG / WorkBoy 风格外观 ========== */
/* 设计参考:原版 Game Boy 的“灰机身 + 蓝/洋红点缀”配色,以及社区常见的 DMG 键盘配色(例如 GMK DMG 系列)。 */
.wrapped-theme-gameboy .keyboard-outer {
background: #c4c1bd;
box-shadow:
0 0 0 4px #2b2b2b,
0 0 0 8px #e8e4e2,
0 0 0 10px #2b2b2b,
inset 0 0 0 2px rgba(255,255,255,0.35);
}
.wrapped-theme-gameboy .keyboard-inner {
background: #d6d2ce;
box-shadow:
inset 4px 4px 0 #a9a39f,
inset -4px -4px 0 #f5f2ee;
}
/* 顶部信息条做成“屏幕窗” */
.wrapped-theme-gameboy .keyboard-header {
margin-bottom: 8px;
padding: 8px 10px;
border: 2px solid #081820;
background: #e0f8d0;
box-shadow:
inset -2px -2px 0 #88c070,
inset 2px 2px 0 #f8f8f8;
}
/* 左侧指示灯:保留一个“电量灯”,其余隐藏 */
.wrapped-theme-gameboy .dot {
width: 8px;
height: 8px;
border-radius: 0;
box-shadow:
0 0 0 2px #081820,
inset 1px 1px 0 rgba(255,255,255,0.25);
}
.wrapped-theme-gameboy .dot-red {
background: #9a2257;
animation: none !important;
}
.wrapped-theme-gameboy .dot-yellow,
.wrapped-theme-gameboy .dot-green {
display: none;
}
.wrapped-theme-gameboy .keyboard-dots::after {
content: 'BATTERY';
margin-left: 6px;
font-family: var(--font-pixel-10), 'Courier New', monospace;
font-size: 8px;
letter-spacing: 1px;
color: #081820;
}
.wrapped-theme-gameboy .keyboard-hint,
.wrapped-theme-gameboy .keyboard-stats {
color: #081820;
text-shadow: none;
}
/* 键盘底板:偏灰,避免整块“全绿”导致像素感过强 */
.wrapped-theme-gameboy .keyboard-body {
background:
repeating-linear-gradient(
0deg,
#bdb8b4 0px, #bdb8b4 2px,
#c9c4c0 2px, #c9c4c0 4px
);
box-shadow:
inset 3px 3px 0 #a9a39f,
inset -3px -3px 0 #f5f2ee;
}
/* 功能键给一点“蓝色键帽”点缀(更像 DMG 配色键盘) */
.wrapped-theme-gameboy .kb-func .kb-key-top {
background: #494786 !important;
box-shadow:
inset -3px -3px 0 #2f2d3a,
inset 3px 3px 0 #6a66a2,
inset -1px -1px 0 #1b1a22,
inset 1px 1px 0 #8a86d0 !important;
}
.wrapped-theme-gameboy .kb-func .kb-label,
.wrapped-theme-gameboy .kb-func .kb-sub {
color: #e0f8d0 !important;
text-shadow: 1px 1px 0 rgba(0,0,0,0.35);
}
/* “音响孔”点阵 */
.wrapped-theme-gameboy .keyboard-outer::before {
content: '';
position: absolute;
right: 10px;
bottom: 12px;
width: 52px;
height: 18px;
background:
radial-gradient(circle, rgba(8, 24, 32, 0.35) 35%, transparent 36%) 0 0 / 6px 6px;
opacity: 0.85;
pointer-events: none;
z-index: 2;
}
/* 品牌文字:换成更贴近主题的“WorkBoy”梗 */
.wrapped-theme-gameboy .keyboard-brand {
position: relative;
color: transparent;
text-shadow: none;
}
.wrapped-theme-gameboy .keyboard-brand::before {
content: 'WECHAT WORKBOY';
color: rgba(8, 24, 32, 0.7);
font-family: var(--font-pixel-10), 'Courier New', monospace;
letter-spacing: 2px;
}
/* ========== DOS 终端主题 - 黑底绿字键盘 ========== */
.wrapped-theme-dos .keyboard-outer {
border-radius: 0;
background: #000000;
border: 2px solid #33ff33;
padding: 4px;
box-shadow:
0 0 10px rgba(51, 255, 51, 0.3),
inset 0 0 20px rgba(51, 255, 51, 0.05);
}
.wrapped-theme-dos .keyboard-inner {
border-radius: 0;
background: #0a0a0a;
border: 1px solid #22aa22;
padding: 6px;
}
.wrapped-theme-dos .keyboard-header {
border-bottom: 1px solid #22aa22;
padding-bottom: 6px;
margin-bottom: 6px;
}
.wrapped-theme-dos .dot {
border-radius: 0;
width: 8px;
height: 8px;
}
.wrapped-theme-dos .dot-red { background: #ff3333; box-shadow: 0 0 5px #ff3333; }
.wrapped-theme-dos .dot-yellow { background: #ffaa00; box-shadow: 0 0 5px #ffaa00; }
.wrapped-theme-dos .dot-green { background: #33ff33; box-shadow: 0 0 5px #33ff33; }
.wrapped-theme-dos .keyboard-stats,
.wrapped-theme-dos .keyboard-hint {
font-family: 'Courier New', 'Consolas', monospace;
color: #33ff33;
text-shadow: 0 0 5px #33ff33;
}
.wrapped-theme-dos .keyboard-body {
border-radius: 0;
background: #050505;
box-shadow: inset 0 0 10px rgba(51, 255, 51, 0.1);
}
.wrapped-theme-dos .kb-key::before {
border-radius: 0;
background: #111111;
}
.wrapped-theme-dos .kb-key-top {
border-radius: 0;
border: 1px solid #33ff33 !important;
background: #0a0a0a !important;
box-shadow:
0 0 3px rgba(51, 255, 51, 0.3),
inset 0 0 5px rgba(51, 255, 51, 0.1) !important;
}
.wrapped-theme-dos .kb-label,
.wrapped-theme-dos .kb-sub {
font-family: 'Courier New', 'Consolas', monospace;
color: #33ff33 !important;
text-shadow: 0 0 3px #33ff33;
filter: none !important;
opacity: 1 !important;
}
.wrapped-theme-dos .kb-space-bar {
border-radius: 0;
background: #33ff33;
box-shadow: 0 0 5px #33ff33;
height: 2px;
}
.wrapped-theme-dos .keyboard-brand {
font-family: 'Courier New', 'Consolas', monospace;
color: #22aa22;
text-shadow: 0 0 3px #22aa22;
}
/* DOS 磨损效果 - 发光强度变化 */
.wrapped-theme-dos .kb-level-1 .kb-key-top,
.wrapped-theme-dos .kb-level-2 .kb-key-top {
box-shadow: 0 0 5px rgba(51, 255, 51, 0.4) !important;
}
.wrapped-theme-dos .kb-level-3 .kb-key-top,
.wrapped-theme-dos .kb-level-4 .kb-key-top {
box-shadow: 0 0 8px rgba(51, 255, 51, 0.5) !important;
}
.wrapped-theme-dos .kb-level-5 .kb-key-top,
.wrapped-theme-dos .kb-level-6 .kb-key-top {
box-shadow: 0 0 10px rgba(51, 255, 51, 0.6) !important;
}
.wrapped-theme-dos .kb-level-7 .kb-key-top,
.wrapped-theme-dos .kb-level-8 .kb-key-top {
box-shadow: 0 0 12px rgba(51, 255, 51, 0.7) !important;
border-color: #44ff44 !important;
}
.wrapped-theme-dos .kb-level-9 .kb-key-top {
box-shadow: 0 0 15px rgba(51, 255, 51, 0.8) !important;
border-color: #55ff55 !important;
}
.wrapped-theme-dos .kb-level-10 .kb-key-top {
opacity: 0 !important;
}
.wrapped-theme-dos .kb-level-10::before {
background: #000000 !important;
border: 1px dashed #22aa22;
}
.wrapped-theme-dos .kb-level-10::after {
content: 'X';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: auto;
height: auto;
background: none;
color: #ff3333;
font-family: 'Courier New', monospace;
font-size: 10px;
text-shadow: 0 0 5px #ff3333;
}
/* ========== Win98 主题 - 键盘外观 ========== */
.wrapped-theme-win98 .keyboard-outer {
border-radius: 0;
background: #c0c0c0;
border: 1px solid #808080;
padding: 4px;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .keyboard-inner {
border-radius: 0;
background: #dfdfdf;
border: 1px solid #808080;
padding: 6px;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .keyboard-header {
border-bottom: 1px solid rgba(0, 0, 0, 0.18);
padding-bottom: 6px;
margin-bottom: 6px;
}
.wrapped-theme-win98 .dot {
border-radius: 0;
width: 8px;
height: 8px;
box-shadow: none;
}
.wrapped-theme-win98 .dot-red { background: #800000; }
.wrapped-theme-win98 .dot-yellow { background: #808000; }
.wrapped-theme-win98 .dot-green { background: #008000; }
.wrapped-theme-win98 .keyboard-stats,
.wrapped-theme-win98 .keyboard-hint {
font-family: inherit;
color: rgba(0, 0, 0, 0.72);
text-shadow: none;
}
.wrapped-theme-win98 .keyboard-body {
border-radius: 0;
background: #ffffff;
border: 1px solid #808080;
box-shadow:
inset 1px 1px 0 #000000,
inset -1px -1px 0 #ffffff;
}
.wrapped-theme-win98 .kb-key::before {
border-radius: 0;
background: #808080;
}
.wrapped-theme-win98 .kb-key-top {
border-radius: 0;
border: 1px solid #808080 !important;
background: #c0c0c0 !important;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000 !important;
}
.wrapped-theme-win98 .kb-label,
.wrapped-theme-win98 .kb-sub {
font-family: inherit;
color: #000000 !important;
text-shadow: none !important;
filter: none !important;
opacity: 1 !important;
}
.wrapped-theme-win98 .kb-space-bar {
border-radius: 0;
background: #000080;
box-shadow: none;
height: 2px;
}
.wrapped-theme-win98 .keyboard-brand {
font-family: inherit;
color: rgba(0, 0, 0, 0.35);
text-shadow: none;
}
/* DOS 聊天气泡主题适配 */
.wrapped-theme-dos .bubble-left,
.wrapped-theme-dos .bubble-right {
background: #0a0a0a;
border: 1px solid #33ff33;
box-shadow: 0 0 5px rgba(51, 255, 51, 0.2);
}
.wrapped-theme-dos .bubble-left::before {
border-right-color: #0a0a0a;
filter: drop-shadow(-1px 0 0 #33ff33);
}
.wrapped-theme-dos .bubble-right {
background: #0a0a0a;
border-color: #33ff33;
}
.wrapped-theme-dos .bubble-right::after {
border-left-color: #0a0a0a;
filter: drop-shadow(1px 0 0 #33ff33);
}
.wrapped-theme-dos .avatar-box {
background: #0a0a0a;
border-color: #33ff33;
}
.wrapped-theme-dos .avatar-box svg {
stroke: #33ff33;
}
/* ========== Game Boy 主题 - 聊天气泡适配 ========== */
/* 聊天区域背景 */
.wrapped-theme-gameboy .rounded-2xl.border.bg-\[\#F5F5F5\] {
background: #9bbc0f !important;
border: 4px solid #306230 !important;
border-radius: 0 !important;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #c5d870;
}
/* 气泡 - 左侧 */
.wrapped-theme-gameboy .bubble-left {
background: #8bac0f;
border: 3px solid #306230;
border-radius: 0;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #9bbc0f;
}
.wrapped-theme-gameboy .bubble-left::before {
border-right-color: #8bac0f;
filter: none;
}
/* 气泡 - 右侧 */
.wrapped-theme-gameboy .bubble-right {
background: #9bbc0f;
border: 3px solid #306230;
border-radius: 0;
box-shadow:
inset -2px -2px 0 0 #306230,
inset 2px 2px 0 0 #c5d870;
}
.wrapped-theme-gameboy .bubble-right::after {
border-left-color: #9bbc0f;
filter: none;
}
/* 头像 */
.wrapped-theme-gameboy .avatar-box {
background: #9bbc0f;
border: 2px solid #306230;
border-radius: 0;
}
.wrapped-theme-gameboy .avatar-box svg {
stroke: #0f380f;
}
/* 文字样式 */
.wrapped-theme-gameboy .bubble-left .wrapped-label,
.wrapped-theme-gameboy .bubble-right .wrapped-label {
color: #306230 !important;
}
.wrapped-theme-gameboy .bubble-left .wrapped-number,
.wrapped-theme-gameboy .bubble-right .wrapped-number {
color: #0f380f !important;
font-family: var(--font-pixel-10), 'Courier New', monospace;
}
.wrapped-theme-gameboy .bubble-left .wrapped-body,
.wrapped-theme-gameboy .bubble-right .wrapped-body {
color: #306230 !important;
}
/* ========== Win98 主题 - 聊天气泡适配 ========== */
/* 聊天区域背景 */
.wrapped-theme-win98 .rounded-2xl.border.bg-\[\#F5F5F5\] {
background: #c0c0c0 !important;
border: 1px solid #808080 !important;
border-radius: 0 !important;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
/* 气泡 - 左侧 */
.wrapped-theme-win98 .bubble-left {
background: #ffffff;
border: 1px solid #808080;
border-radius: 0;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .bubble-left::before {
border-right-color: #ffffff;
filter: none;
}
/* 气泡 - 右侧 */
.wrapped-theme-win98 .bubble-right {
background: #dfdfdf;
border: 1px solid #808080;
border-radius: 0;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .bubble-right::after {
border-left-color: #dfdfdf;
filter: none;
}
/* 头像 */
.wrapped-theme-win98 .avatar-box {
background: #c0c0c0;
border-color: #808080;
border-radius: 0;
box-shadow:
inset 1px 1px 0 #ffffff,
inset -1px -1px 0 #000000;
}
.wrapped-theme-win98 .avatar-box svg {
stroke: #000080;
}
/* 文字样式(气泡内需要更“黑白”) */
.wrapped-theme-win98 .bubble-left .wrapped-label,
.wrapped-theme-win98 .bubble-right .wrapped-label {
color: rgba(0, 0, 0, 0.65) !important;
}
.wrapped-theme-win98 .bubble-left .wrapped-number,
.wrapped-theme-win98 .bubble-right .wrapped-number {
color: #000080 !important;
}
.wrapped-theme-win98 .bubble-left .wrapped-body,
.wrapped-theme-win98 .bubble-right .wrapped-body {
color: rgba(0, 0, 0, 0.85) !important;
}
/* ========== DOS 主题 - 聊天气泡文字适配 ========== */
.wrapped-theme-dos .bubble-left .wrapped-label,
.wrapped-theme-dos .bubble-right .wrapped-label {
color: #22aa22 !important;
text-shadow: 0 0 3px #22aa22;
font-family: 'Courier New', monospace;
}
.wrapped-theme-dos .bubble-left .wrapped-number,
.wrapped-theme-dos .bubble-right .wrapped-number {
color: #33ff33 !important;
text-shadow: 0 0 5px #33ff33;
font-family: 'Courier New', monospace;
}
.wrapped-theme-dos .bubble-left .wrapped-body,
.wrapped-theme-dos .bubble-right .wrapped-body {
color: #33ff33 !important;
text-shadow: 0 0 3px #33ff33;
font-family: 'Courier New', monospace;
}
</style>
@@ -59,8 +59,7 @@
</template>
<script setup>
import { themedHeatColor, maxInMatrix, formatHourRange } from '~/utils/wrapped/heatmap'
import { useWrappedTheme } from '~/composables/useWrappedTheme'
import { heatColor, maxInMatrix, formatHourRange } from '~/utils/wrapped/heatmap'
const props = defineProps({
weekdayLabels: { type: Array, default: () => ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] },
@@ -69,8 +68,6 @@ const props = defineProps({
totalMessages: { type: Number, default: 0 }
})
const { theme } = useWrappedTheme()
const matrixSafe = computed(() => {
// Expect 7x24, but keep defensive to avoid UI crashes.
const m = Array.isArray(props.matrix) ? props.matrix : []
@@ -93,7 +90,7 @@ const timeLabels = computed(() => {
return labels
})
const colorFor = (v) => themedHeatColor(v, maxValue.value, theme.value)
const colorFor = (v) => heatColor(v, maxValue.value)
const tooltipFor = (weekdayIndex, hour, v) => {
const w = props.weekdayLabels?.[weekdayIndex] ?? `${weekdayIndex + 1}`
@@ -104,7 +101,7 @@ const tooltipFor = (weekdayIndex, hour, v) => {
const legendColor = (i) => {
const t = i / 6
return themedHeatColor(Math.max(1, t * (maxValue.value || 1)), maxValue.value || 1, theme.value)
return heatColor(Math.max(1, t * (maxValue.value || 1)), maxValue.value || 1)
}
const originFor = (weekdayIndex, hour) => {
@@ -115,57 +112,3 @@ const originFor = (weekdayIndex, hour) => {
return `${x} ${y}`
}
</script>
<style>
/* ========== Game Boy 主题 ========== */
.wrapped-theme-gameboy .heatmap-cell {
border-radius: 0 !important;
}
.wrapped-theme-gameboy .wrapped-label,
.wrapped-theme-gameboy .wrapped-body {
color: #306230 !important;
}
.wrapped-theme-gameboy .wrapped-number {
color: #0f380f !important;
}
.wrapped-theme-gameboy .heatmap-legend-cell {
border-radius: 0 !important;
}
/* ========== DOS 主题 ========== */
.wrapped-theme-dos .heatmap-cell {
border-radius: 0 !important;
box-shadow: 0 0 2px rgba(51, 255, 51, 0.3);
}
.wrapped-theme-dos .wrapped-label,
.wrapped-theme-dos .wrapped-body {
color: #22aa22 !important;
text-shadow: 0 0 3px #22aa22;
}
.wrapped-theme-dos .wrapped-number {
color: #33ff33 !important;
text-shadow: 0 0 5px #33ff33;
}
.wrapped-theme-dos .heatmap-legend-cell {
border-radius: 0 !important;
}
/* ========== Win98 主题 ========== */
.wrapped-theme-win98 .heatmap-cell {
border-radius: 0 !important;
border: 1px solid rgba(0, 0, 0, 0.12);
}
.wrapped-theme-win98 .heatmap-legend-cell {
border-radius: 0 !important;
border: 1px solid rgba(0, 0, 0, 0.12);
}
</style>
-31
View File
@@ -1,31 +0,0 @@
/**
* 年度总结页面主题管理 composable
* 仅保留 modern(现代)主题
*/
// 全局响应式状态(跨组件共享)
// Note: 历史上曾尝试过 gameboy / win98 等主题,但目前已移除,仅保留 Modern。
const theme = ref('off') // off === Modern
export function useWrappedTheme() {
const setTheme = (newTheme) => {
// Only keep Modern.
if (newTheme !== 'off') {
console.warn(`Wrapped theme '${newTheme}' has been removed; falling back to Modern.`)
}
theme.value = 'off'
}
const cycleTheme = () => setTheme('off')
const isRetro = computed(() => false)
const themeClass = computed(() => '')
return {
theme: readonly(theme),
setTheme,
cycleTheme,
isRetro,
themeClass
}
}
+2 -6
View File
@@ -2,7 +2,6 @@
<div
ref="deckEl"
class="wrapped-deck-root relative h-screen w-full overflow-hidden transition-colors duration-500"
:class="themeClass"
:style="{ backgroundColor: currentBg }"
>
<!-- PPT 风格单张卡片占据全页面鼠标滚轮切换 -->
@@ -68,7 +67,7 @@
<!-- 右上角年份选择器主题化 -->
<div class="absolute top-6 right-6 z-20 pointer-events-auto select-none">
<div class="relative">
<div v-if="!isRetro" class="absolute -inset-6 rounded-full bg-[#07C160]/10 blur-2xl"></div>
<div class="absolute -inset-6 rounded-full bg-[#07C160]/10 blur-2xl"></div>
<div class="relative flex justify-end">
<WrappedYearSelector
v-if="yearOptions.length > 1"
@@ -77,7 +76,7 @@
/>
<div v-else class="wrapped-label text-xs text-[#00000066]">{{ year }}</div>
</div>
<div v-if="!isRetro" class="relative mt-1 h-[1px] w-16 ml-auto bg-gradient-to-l from-[#07C160]/40 to-transparent"></div>
<div class="relative mt-1 h-[1px] w-16 ml-auto bg-gradient-to-l from-[#07C160]/40 to-transparent"></div>
</div>
</div>
@@ -204,9 +203,6 @@ const year = ref(Number(route.query?.year) || new Date().getFullYear())
// 分享视图不展示账号信息:默认让后端自动选择;需要指定时可用 query ?account=wxid_xxx
const account = ref(typeof route.query?.account === 'string' ? route.query.account : '')
// 主题:仅保留 Modern
const { isRetro, themeClass } = useWrappedTheme()
const accounts = ref([])
const accountsLoading = ref(true)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

View File
@@ -1,94 +0,0 @@
Copyright (c) 2021, TakWolf (https://takwolf.com),
with Reserved Font Name "Ark Pixel".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -1,110 +0,0 @@
[BoutiqueBitmap7x7]
2020-2022《字言字語》Cen-cyun, Liu. Luke Liu.
https://fontspeech.blogspot.com/
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
此字型是免費的。
無論您是否進行對本字型進行商業或非商業性修改,均可無限制地使用,複製和分發它們。
本字型的衍生品之授權必須與此字型相同,且不作任何擔保。
[MisakiGothic]
Copyright (C) 2002-2019 Num Kadoma
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
これらのフォントはフリー(自由な)ソフトウエアです。
あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
[观致]
这是日文字体Misaki基础上补充的
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -1,122 +0,0 @@
[BoutiqueBitmap9x9]
Copyright © 2025 字言字型 版權所有。
Copyright © 2025 fancy type foundry. All rights reserved.
2020-2025《字言字語》Cen-cyun, Liu. Luke Liu.
https://fontspeech.blogspot.com/
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
此字型是免費的。
無論您是否進行對本字型進行商業或非商業性修改,均可無限制地使用,複製和分發它們。
本字型的衍生品之授權必須與此字型相同,且不作任何擔保。
此字体是免费的。
无论您是否进行对本字型进行商业或非商业性修改,均可无限制地使用,复制和分发它们。
本字型的衍生品之授权必须与此字型相同,且不作任何担保。
[M+ BITMAP FONTS]
Copyright (C) 2002-2004 COZ
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute it, with or without modification, either commercially and noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
これらのフォントはフリー(自由な)ソフトウエアです。
あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
[ベストテン(BestTen-DOT]
フリーフォントのベストテンは無料でダウンロードできるドットフォント。商用・非商用問わず使用可能なので、安心してダウンロードしてください。
作成した印刷物およびデジタル・コンテンツにつき、その商用・非商用にかかわらず印刷、放送、通信、各種記録メディアなどの媒体の形式も問わず、使用をすることができます。プログラムへの埋め込みが可能です。
このフォントのライセンスは、
M+のライセンスに準じます。
M+ FONT LICENSEについては、配布物に含まれる
mplus_bitmap_fonts をご覧ください。
[Fusion Pixel]
Copyright (c) 2022, TakWolf (https://takwolf.com), with Reserved Font Name 'Fusion Pixel'.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-108
View File
@@ -1,108 +0,0 @@
[Cubic 11]
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
此字型是免費的。
無論您是否進行對本字型進行商業或非商業性修改,均可無限制地使用,複製和分發它們。
本字型的衍生品之授權必須與此字型相同,且不作任何擔保。
[JF Dot M+H 12]
Copyright(c) 2005 M+ FONTS PROJECT
[M+ BITMAP FONTS]
Copyright (C) 2002-2004 COZ
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute it, with or without modification, either commercially and noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
これらのフォントはフリー(自由な)ソフトウエアです。
あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-93
View File
@@ -1,93 +0,0 @@
Copyright (c) 20192025 Lee Minseo (quiple@quiple.dev)
This font software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-258
View File
@@ -1,258 +0,0 @@
===============================================================================
8×8 ドット日本語フォント「美咲フォント」
(2021-05-05 版)
Copyright(C) 2002-2021 Num Kadoma
===============================================================================
-------------------------------------------------------------------------------
概要
-------------------------------------------------------------------------------
 美咲フォントは 8×8 ドットの日本語ビットマップフォントです。
 JIS第一・第二水準をサポートしています。
 一部の記号を除いた全ての文字は 7×7 ドットの範囲に収まっているため、
 文字同士を隣接させても行間・字間が確保できます。
 フォント名は制作のきっかけとなった美咲 礼威氏からいただきました。
-------------------------------------------------------------------------------
バリエーション
-------------------------------------------------------------------------------
・「美咲明朝」は、美咲ゴシックの非漢字部を明朝風の字形に差し替えたものです。
・「美咲ゴシック第2」は美咲ゴシックをベースに、半角文字は縦 7 px、
 全角仮名は横 7 px をいっぱいに使う字形に差し替えたものです。
 各機種用のアーカイブに、差替え済みのフォントファイルを同梱してあります。
-------------------------------------------------------------------------------
ライセンス
-------------------------------------------------------------------------------
 These fonts are free softwares.
 Unlimited permission is granted to use, copy, and distribute it, with or without modification, either commercially and noncommercially.
 THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
 これらのフォントはフリー(自由な)ソフトウエアです。
 あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
-------------------------------------------------------------------------------
制作履歴
-------------------------------------------------------------------------------
■2021-05-05
・文字を追加
 共通: ␣
・字形を修正・変更
 共通: №
 ゴシック第2: プヴ
・(TTF) ビットマップを埋込み
■2019-10-19
・ドキュメントの文字コードを UTF-8N、改行コードを LF に統一
・(TTF, BDF) 記号を追加:
  ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉▔▕╭╮╰╯♠♥♦♣╱╲╳♤♡♢♧
■2019-08-18
・各ファイルのパーミッションから、不要な実行ビットを除去 (フォントの変更はなし)
■2019-06-03
・新フォント「美咲ゴシック第2」を追加。英語名「MisakiGothic2nd」
・「令和」の合字を追加
・各種字形の修正
・(E500) ファイル名のつけ方を変更して配布を再開
・(BDF) 一部仕様を変更して配布を再開
  エンコーディングは ISO 10646-1 のみとし、全ての文字を 1 ファイルに収録
  XLFD 変更
  BITMAP を文字の上下左右端の空白を削った形とし、BBX で位置を指定する形に修正
■2019-02-03a
・制作履歴における、変更した文字の抜け (ゴシックの「と」「ね」「ゐ」) を修正
■2019-02-03
・ゴシック・明朝共通の変更: &,.‘’()〔〕[]{}〈〉【】°′″№0索二
  半角 (「&」字形変更)
  記号類 (主に括弧類の位置調整)
  数字 (ゼロにスラッシュを追加)
  漢字 (「索」「二」の字形変更)
・ゴシックのみの変更: 69CGOQΟОСЭぐぜとどねばぶゐガグゾダバヴ
  数字 (「6」「9」の先端を伸ばす形に変更)
  アルファベット (オーなどの丸み変更)
  仮名 (主に濁点/半濁点つき文字の調整)
・明朝のみの変更: ぐでとどなぶウグケサザソゾダドワヱヴЁ
  仮名 (各種字形変更)
  キリル (「Ё」の字形変更)
■2015-04-10
・誤字になっていた「喪」を修正
・(TTF) ウェイトを「Regular」に変更
  一部の記号を追加、変更
  バージョン番号を「yyyy.mmdd」の形に変更
  その他、パラメータを見直し
  埋込みビットマップ版の同梱を取り止め
■2012-06-03
・正式公開初版
■2012-03-31
・各種ファイル名、ドキュメントの見直し
・BDF 版以外のアーカイブ形式を zip に変更
・(TTF) アウトライン版の正式採用、Mac におそらく対応
・(FONTX) 4X8.FNT の 00h1Fh に DOS/V 罫線を追加
■2011-03-08
・(TTF-Outline) 生成手順の改善に伴い periodβ11 作成・公開
■2010-03-07
・(BDF) FOUNDRY, POINT_SIZE, AVERAGE_WIDTH, SWIDTH, XLFD, 各ファイル名の変更
■2006-10-01
・(TTF-Outline) 試作版配布開始
■2006-04-01
・(PNG) 配布開始
■2006-03-26
・(E500) 13 区追加版・軽量版 (第一水準のみ) 同梱開始
  フォントファイルの命名規則変更
・(BDF) 美咲明朝の配布形態をゴシックとの差分から実ファイル同梱へ
  BITMAP の記述方法を修正 (16 進 4 桁から 2 桁へ)
・(FONTX) 配布再開
■2005-07-20
・アーカイブの命名規則を変更
・(TTF, BDF) 著作権表記を変更
・(BDF) 1 バイト文字フォントのファイル名をそれぞれ 4x8.bdf, 4x8_8859.bdf に
■2004-09-12
・(TTF, BDF) 13 区の文字追加
■2004-06-24
・フォントの名称変更
・(美咲フォントを美咲ゴシック、ファミリーの総称を美咲フォントに)
■2004-05-09
・美咲明朝の同梱開始
■2004-04-19
・半濁点の処理を変更
■2004-04-11
・3 フォント同時編集ツール「"SCRNJPN.FNT" Editor」完成
■2004-02-09
・濁点の処理を変更
■2003-09-21
・制作履歴の日付ミスを修正 (美咲氏 Thanx!)
■2003-06-04
・ライセンスに関する記述を変更
■2003-05-04
・(BDF) 3x7_8859.bdf の CHARS の値を修正
・(BDF) 3x7_8859.bdf のファイル名が 3X7_8859.bdf になっていたのを修正
■2003-03-26
・マニュアルをフォントの説明と各形式の説明に分離
■2003-01-02
・各部のフォント間での統一化作業を本格的に開始
■2002-06-10
・JIS X 0208-1983 での文字の変更、JIS X 0208-1990 での文字の追加に対応
■2002-06-06
・未定義文字のフォントを 3×3 の ■ に
■2002-06-01
・第二水準完成
■2002-05-22
・制作再開
■2002-05-21
・フォント作成専用ツール「'SCRNJPN.FNT' Editor」完成
■2002-05-11
・E650 暴走により第二水準中の 600 文字近くが消滅→制作中断
■2002-04-初
・第一水準完成
■2002-03-中
・制作開始
-------------------------------------------------------------------------------
バージョン情報
-------------------------------------------------------------------------------
■2021-05-05
・美咲フォント 2021-05-05 版 (E500/BDF/TTF/PNG)
■2019-10-19
・美咲フォント 2019-10-19 版 (E500/BDF/TTF/PNG)
■2019-08-18
・美咲フォント 2019-06-03a 版 (E500/BDF/TTF/PNG)
■2019-06-03
・美咲フォント 2019-06-03 版 (E500/BDF/TTF/PNG)
■2019-02-03
・美咲フォント 2019-02-03 版 (TTF/PNG)
■2015-04-10
・美咲フォント 2015-04-10 版 (TTF/PNG)
■2012-06-03
・美咲フォント 2012-06-03 版 (E500/BDF/TTF/Ruputer/FONTX/PNG)
■2012-03-31
・美咲フォント periodβ12 (E500/BDF/TTF/Ruputer/FONTX/PNG)
■2010-03-07
・美咲フォント periodβ11a (BDF)
■2008-06-03
・美咲フォント periodβ11 (E500/BDF/TTF/Ruputer/FONTX/PNG/TTF-Outline)
■2006-03-26
・美咲フォント periodβ10 (E500/BDF/TTF/Ruputer/FONTX/PNG/TTF-Outline)
■2005-07-20
・美咲フォント periodβ9 (E500/BDF/TTF/Ruputer)
■2004-05-09
・美咲フォント periodβ8 (E500/BDF/TTF/Ruputer)
■2003-09-21
・美咲フォント periodβ7 (E500/BDF/TTF/Ruputer)
■2003-06-04
・美咲フォント periodβ6 (E500/BDF/TTF/Ruputer)
■2003-05-04
・美咲フォント periodβ5 (E500/BDF/TTF/Ruputer)
■2003-03-26
・美咲フォント periodβ4 (E500/BDF/TTF/Ruputer/FONTX)
■2002-12-14
・美咲フォント periodβ3 (E500)
■2002-06-18
・美咲フォント periodβ2 (E500)
■2002-06-03
・美咲フォント periodβ1 (E500)
-------------------------------------------------------------------------------
連絡先
-------------------------------------------------------------------------------
 門真 なむ (Num Kadoma)
 ・Twitter: @num_kadoma
 ・Website: http://littlelimit.net/
@@ -1,92 +0,0 @@
Copyright (c) 2023-2024 Mark Li (itmarkibfb@gmail.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-94
View File
@@ -1,94 +0,0 @@
Copyright (c) 2022, TakWolf (https://takwolf.com),
with Reserved Font Name "Fusion Pixel".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

-33
View File
@@ -38,39 +38,6 @@ export const heatColor = (value, max) => {
return `hsl(${hue.toFixed(1)} ${sat}% ${light.toFixed(1)}%)`
}
// Theme-aware heat color function
export const themedHeatColor = (value, max, theme) => {
const v = Number(value) || 0
const m = Number(max) || 0
const t = (v > 0 && m > 0) ? clamp01(Math.sqrt(v / m)) : 0
switch (theme) {
case 'gameboy': {
// Game Boy 4-color palette: #0f380f, #306230, #8bac0f, #9bbc0f
if (t === 0) return '#9bbc0f'
if (t < 0.33) return '#8bac0f'
if (t < 0.66) return '#306230'
return '#0f380f'
}
case 'dos': {
// DOS green phosphor: from dark to bright green
if (t === 0) return 'rgba(51, 255, 51, 0.1)'
const light = 20 + 60 * t
return `hsl(120 100% ${light.toFixed(1)}%)`
}
case 'win98': {
// Win98-ish "system colors": gray -> blue highlight
if (t === 0) return '#dfdfdf'
if (t < 0.33) return '#c0c0c0'
if (t < 0.66) return '#808080'
return '#000080'
}
default:
// Modern (off) - use original heatColor
return heatColor(value, max)
}
}
export const formatHourRange = (hour) => {
const h = Number(hour)
if (!Number.isFinite(h)) return ''