Files

733 lines
36 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: data:; script-src 'self' 'unsafe-inline'; connect-src https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https: *;">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%236366f1'/%3E%3Cstop offset='1' stop-color='%2322d3ee'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='14' fill='url(%23g)'/%3E%3C/svg%3E">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#05070f">
<title>hyonglin.top</title>
<style>
/* ===== 主题:默认深色(主视觉),可切换浅色并记住 ===== */
:root {
--bg: #05070f;
--bg-2: #0a0f1f;
--text: #f0f4ff;
--muted: #93a0c0;
--card: rgba(148,163,216,.07);
--card-2: rgba(148,163,216,.025);
--border: rgba(148,180,255,.13);
--inset: rgba(8,12,24,.55);
--inner: inset 0 1px 0 rgba(255,255,255,.07);
--shadow: 0 1px 2px rgba(0,0,0,.5), 0 12px 34px rgba(0,0,0,.45);
--shadow-hover: 0 2px 6px rgba(0,0,0,.55), 0 22px 52px rgba(0,0,0,.6);
--grad: linear-gradient(120deg, #818cf8, #38bdf8 55%, #22d3ee);
--line: rgba(148,180,255,.07);
--noise-op: .06;
--aurora-op: .5;
--sky-a: rgba(59,130,246,.38); --sky-b: rgba(34,211,238,.16);
--orb-op: .5;
--ga: rgba(99,102,241,.30);
--gb: rgba(34,211,238,.22);
--spot: rgba(129,140,248,.14);
}
:root[data-theme="light"] {
--bg: #eef2fa;
--bg-2: #e4eaf6;
--text: #101728;
--muted: #5c6a84;
--card: rgba(255,255,255,.62);
--card-2: rgba(255,255,255,.4);
--border: rgba(15,30,70,.09);
--inset: rgba(255,255,255,.85);
--inner: inset 0 1px 0 rgba(255,255,255,.65);
--shadow: 0 1px 2px rgba(30,50,100,.05), 0 10px 30px rgba(30,50,100,.08);
--shadow-hover: 0 2px 4px rgba(30,50,100,.06), 0 18px 44px rgba(30,50,100,.16);
--grad: linear-gradient(120deg, #6366f1, #0ea5e9 55%, #22d3ee);
--line: rgba(20,40,90,.055);
--noise-op: .035;
--aurora-op: .28;
--sky-a: #dbeafe; --sky-b: #cffafe;
--orb-op: .5;
--ga: rgba(99,102,241,.16);
--gb: rgba(34,211,238,.14);
--spot: rgba(99,102,241,.10);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
html { scroll-behavior: smooth; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: linear-gradient(180deg, var(--bg), var(--bg-2) 55%, var(--bg));
color: var(--text);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
::selection { background: #6366f1; color: #fff; }
/* ===== 背景分层:极光 + 网格 + 噪点 + 光斑 + 跟随光晕 ===== */
.aurora {
position: fixed; inset: -25%; z-index: 0; pointer-events: none;
opacity: var(--aurora-op); filter: blur(110px); will-change: transform;
background: conic-gradient(from 210deg at 50% 38%,
rgba(99,102,241,.42), rgba(34,211,238,.30), rgba(217,70,239,.30),
rgba(56,189,248,.34), rgba(99,102,241,.42));
-webkit-mask-image: radial-gradient(ellipse 62% 52% at 50% 26%, #000 18%, transparent 68%);
mask-image: radial-gradient(ellipse 62% 52% at 50% 26%, #000 18%, transparent 68%);
animation: spin 70s linear infinite;
}
@keyframes spin { to { transform: rotate(1turn); } }
.bg-grid {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background-image:
linear-gradient(var(--line) 1px, transparent 1px),
linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 46px 46px;
-webkit-mask-image: radial-gradient(ellipse 100% 78% at 50% 8%, #000 25%, transparent 72%);
mask-image: radial-gradient(ellipse 100% 78% at 50% 8%, #000 25%, transparent 72%);
}
.bg-noise {
position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: var(--noise-op);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
.orb { position: fixed; border-radius: 50%; filter: blur(90px); z-index: 0; pointer-events: none; opacity: var(--orb-op); will-change: transform; }
.orb1 { width: 520px; height: 520px; top: -180px; left: -130px; background: #6366f1; }
.orb2 { width: 460px; height: 460px; bottom: -150px; right: -110px; background: #22d3ee; }
.orb3 { width: 380px; height: 380px; top: 32%; right: -160px; background: #d946ef; opacity: calc(var(--orb-op) * .7); }
.glow { position: fixed; left: 0; top: 0; z-index: 0; pointer-events: none; border-radius: 50%; will-change: transform; opacity: 0; }
.glow-a { width: 560px; height: 560px; background: radial-gradient(circle, var(--ga), transparent 65%); }
.glow-b { width: 420px; height: 420px; background: radial-gradient(circle, var(--gb), transparent 65%); }
/* ===== 顶栏 ===== */
header.topbar {
position: sticky; top: 0; z-index: 10;
display: flex; align-items: center; gap: 14px;
max-width: 1080px; margin: 0 auto; padding: 14px 20px;
}
header.topbar::before {
content: ''; position: absolute; inset: 0; z-index: -1;
background: color-mix(in srgb, var(--bg) 72%, transparent);
backdrop-filter: blur(14px) saturate(1.3);
-webkit-backdrop-filter: blur(14px) saturate(1.3);
border-bottom: 1px solid var(--border);
opacity: 0; transition: opacity .25s ease;
}
header.topbar.stuck::before { opacity: 1; }
.brand {
display: flex; align-items: center; gap: 10px;
font-weight: 800; font-size: 1rem; letter-spacing: -.01em;
color: var(--text); text-decoration: none;
}
.brand .logo {
width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
background: var(--grad);
box-shadow: 0 4px 18px rgba(99,102,241,.5);
transition: transform .3s cubic-bezier(.3,.7,.3,1.4);
}
.brand:hover .logo { transform: rotate(-8deg) scale(1.08); }
.top-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.chip {
display: inline-flex; align-items: center; gap: 8px;
font-size: .74rem; color: var(--muted);
border: 1px solid var(--border); background: var(--inset);
padding: 6px 12px; border-radius: 999px;
}
.dot {
width: 7px; height: 7px; border-radius: 50%; background: #34d399; flex-shrink: 0;
box-shadow: 0 0 0 0 rgba(52,211,153,.6), 0 0 8px rgba(52,211,153,.8);
animation: pulse 2.6s ease-out infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(52,211,153,.6), 0 0 8px rgba(52,211,153,.8); }
70% { box-shadow: 0 0 0 8px rgba(52,211,153,0), 0 0 8px rgba(52,211,153,.8); }
100% { box-shadow: 0 0 0 0 rgba(52,211,153,0), 0 0 8px rgba(52,211,153,.8); }
}
.tbtn {
width: 36px; height: 36px; border-radius: 11px; cursor: pointer;
border: 1px solid var(--border); background: var(--inset);
font-size: 1rem; line-height: 1; color: var(--text);
display: flex; align-items: center; justify-content: center;
transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.tbtn:hover { transform: translateY(-1px); border-color: #818cf8; box-shadow: 0 6px 20px rgba(99,102,241,.3); }
.tbtn:active { transform: scale(.94); }
.wrap { position: relative; z-index: 1; max-width: 1080px; margin: 0 auto; padding: 18px 20px 44px; }
/* ===== Bento 网格 ===== */
.grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.card {
background: linear-gradient(180deg, var(--card), var(--card-2));
border: 1px solid var(--border);
border-radius: 24px;
padding: 22px 24px;
backdrop-filter: blur(16px) saturate(1.3);
-webkit-backdrop-filter: blur(16px) saturate(1.3);
box-shadow: var(--inner), var(--shadow);
position: relative;
overflow: hidden;
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
/* 滚动进入:模糊浮现(IntersectionObserver 触发) */
.card:not(.in) { opacity: 0; }
.card.in { animation: rise .7s cubic-bezier(.2,.7,.3,1) backwards; animation-delay: var(--d, 0s); }
@keyframes rise {
from { opacity: 0; transform: translateY(26px) scale(.965); filter: blur(10px); }
to { opacity: 1; transform: none; filter: blur(0); }
}
/* 跟随光标的卡内聚光 */
.card::before {
content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
background: radial-gradient(380px circle at var(--mx, 50%) var(--my, 50%), var(--spot), transparent 62%);
opacity: 0; transition: opacity .3s ease;
}
.card:hover::before { opacity: 1; }
/* 悬停渐变发光描边 */
.card::after {
content: ''; position: absolute; inset: 0; z-index: 1; border-radius: inherit; pointer-events: none;
padding: 1px;
background: linear-gradient(135deg,
color-mix(in srgb, var(--ac, #818cf8) 75%, transparent), transparent 38%,
transparent 62%, color-mix(in srgb, var(--ac, #22d3ee) 55%, transparent));
-webkit-mask: linear-gradient(#000, #000) content-box, linear-gradient(#000, #000);
-webkit-mask-composite: xor; mask-composite: exclude;
opacity: 0; transition: opacity .3s ease;
}
.card:hover::after { opacity: 1; }
.card:not(.hero):hover {
transform: translateY(-4px);
box-shadow: var(--inner), var(--shadow-hover), 0 0 44px -14px color-mix(in srgb, var(--ac, #818cf8) 45%, transparent);
}
.card > * { position: relative; z-index: 2; }
/* ===== Hero:问候 + 标题 + 搜索(3D 倾斜) ===== */
.hero {
grid-column: span 4; grid-row: span 2;
display: flex; flex-direction: column; justify-content: center;
background:
radial-gradient(460px 320px at 88% -12%, color-mix(in srgb, #818cf8 20%, transparent), transparent 62%),
linear-gradient(180deg, var(--card), var(--card-2));
transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
will-change: transform;
}
.eyebrow {
display: inline-flex; align-items: center; gap: 8px;
font-size: .72rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
color: var(--muted); margin-bottom: 14px;
}
.eyebrow::before { content: ''; width: 20px; height: 2px; border-radius: 2px; background: var(--grad); box-shadow: 0 0 10px rgba(99,102,241,.8); }
.hero h1 {
font-size: clamp(2.8rem, 6.6vw, 4.4rem);
font-weight: 800; letter-spacing: -.04em; line-height: 1.02;
background: var(--grad); background-size: 220% 100%;
-webkit-background-clip: text; background-clip: text; color: transparent;
filter: drop-shadow(0 0 26px rgba(99,102,241,.35));
animation: grademove 9s ease-in-out infinite alternate;
}
@keyframes grademove { to { background-position: 100% 0; } }
.hero p.sub { color: var(--muted); margin-top: 14px; font-size: 1.02rem; }
.hero p.sub strong { color: var(--text); font-weight: 650; }
form.search {
display: flex; align-items: center; gap: 8px;
margin-top: 28px; padding: 6px 6px 6px 16px;
background: var(--inset);
border: 1px solid var(--border);
border-radius: 999px;
transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
form.search:focus-within {
border-color: #818cf8;
box-shadow: 0 0 0 3px rgba(99,102,241,.22), 0 12px 36px rgba(99,102,241,.25);
transform: translateY(-1px);
}
form.search .sicon { font-size: 1rem; opacity: .8; flex-shrink: 0; }
form.search select {
flex-shrink: 0; cursor: pointer;
font-size: .82rem; color: var(--muted);
background: transparent; border: 1px solid var(--border); border-radius: 999px;
padding: 7px 10px; outline: none; max-width: 118px;
}
form.search select:focus-visible { border-color: #818cf8; color: var(--text); }
form.search select option { color: #101728; }
form.search input {
flex: 1; min-width: 0; background: none; border: none; outline: none;
color: var(--text); font-size: .95rem; padding: 9px 0;
}
form.search input::placeholder { color: var(--muted); }
form.search kbd {
flex-shrink: 0; font-family: inherit; font-size: .72rem; color: var(--muted);
border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 6px;
padding: 2px 7px; background: var(--card);
}
form.search button {
border: none; cursor: pointer; flex-shrink: 0;
padding: 10px 26px; border-radius: 999px;
font-size: .9rem; font-weight: 600; color: #fff;
background: var(--grad); background-size: 180% 100%;
box-shadow: 0 6px 22px rgba(99,102,241,.45);
transition: transform .14s ease, filter .14s ease, box-shadow .2s ease, background-position .4s ease;
}
form.search button:hover { transform: scale(1.05); filter: brightness(1.12); background-position: 100% 0; box-shadow: 0 8px 30px rgba(56,189,248,.5); }
form.search button:active { transform: scale(.97); }
/* ===== 天气卡 ===== */
.sky {
grid-column: span 2; grid-row: span 2;
display: flex; flex-direction: column; justify-content: center; gap: 6px;
background: linear-gradient(165deg, var(--sky-a), var(--sky-b));
--ac: #38bdf8;
}
.card .label { font-size: .72rem; color: var(--muted); letter-spacing: .12em; text-transform: uppercase; margin-bottom: 8px; }
.sky .emoji { font-size: 3.6rem; line-height: 1; animation: floaty 4.5s ease-in-out infinite; filter: drop-shadow(0 8px 18px rgba(251,191,36,.35)); }
@keyframes floaty { 50% { transform: translateY(-8px); } }
.sky .temp { font-size: clamp(2.4rem, 4.6vw, 3.1rem); font-weight: 750; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.wcity { font-size: .8rem; color: var(--muted); min-height: 1.2em; }
/* ===== 日期 / 时间卡 ===== */
.clockcard, .datecard { grid-column: span 3; display: flex; flex-direction: column; justify-content: center; }
.clockcard .value { font-size: clamp(2.1rem, 4.6vw, 2.9rem); font-weight: 750; font-variant-numeric: tabular-nums; letter-spacing: .01em; line-height: 1.15; }
.clockcard .value .sec { font-size: .55em; color: var(--muted); font-weight: 600; }
.datecard .value { font-size: clamp(1.35rem, 2.5vw, 1.8rem); font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: .02em; line-height: 1.15; }
.datecard .value .wk { color: var(--muted); font-weight: 600; }
/* ===== 服务分组面板 ===== */
.panel { grid-column: span 3; }
.panel > h2 { display: flex; align-items: center; gap: 8px; font-size: .95rem; font-weight: 700; margin-bottom: 14px; }
.panel > h2 .count { margin-left: auto; font-size: .72rem; font-weight: 600; color: var(--muted); background: var(--inset); border: 1px solid var(--border); padding: 3px 10px; border-radius: 999px; }
ul.tiles { list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; }
ul.tiles a {
display: flex; align-items: center; gap: 10px;
padding: 11px 12px;
background: var(--inset);
border: 1px solid var(--border);
border-radius: 14px;
text-decoration: none; color: var(--text);
position: relative;
transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
ul.tiles a:hover {
transform: translateY(-3px);
border-color: color-mix(in srgb, var(--ac) 60%, transparent);
background: color-mix(in srgb, var(--ac) 8%, var(--inset));
box-shadow: 0 8px 26px color-mix(in srgb, var(--ac) 30%, transparent);
}
ul.tiles a:focus-visible { outline: 2px solid var(--ac); outline-offset: 2px; }
ul.tiles a .go {
margin-left: auto; flex-shrink: 0;
font-size: .85rem; color: var(--ac);
opacity: 0; transform: translate(-5px, 5px);
transition: opacity .18s ease, transform .18s ease;
}
ul.tiles a:hover .go { opacity: 1; transform: none; }
ul.tiles .icon {
font-size: 1.1rem; width: 36px; height: 36px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
border-radius: 11px;
background: linear-gradient(135deg, color-mix(in srgb, var(--ac) 38%, transparent), color-mix(in srgb, var(--ac) 12%, transparent));
box-shadow: 0 0 0 0 color-mix(in srgb, var(--ac) 40%, transparent);
transition: transform .25s cubic-bezier(.3,.7,.3,1.5), box-shadow .25s ease;
}
ul.tiles a:hover .icon { transform: scale(1.12) rotate(-6deg); box-shadow: 0 0 16px 0 color-mix(in srgb, var(--ac) 45%, transparent); }
ul.tiles .meta { display: flex; flex-direction: column; min-width: 0; }
ul.tiles .name { font-weight: 650; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
ul.tiles .desc { color: var(--muted); font-size: .72rem; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 分组配色 */
.s-self { --ac: #34d399; }
.s-ai { --ac: #a78bfa; }
.s-cloud { --ac: #60a5fa; }
.s-util { --ac: #fbbf24; }
.s-game { --ac: #f472b6; grid-column: span 6; }
ul.tiles li.wide { grid-column: 1 / -1; }
/* ===== 页脚 ===== */
footer.card { background: none; border: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none; padding: 14px 0 0; display: flex; flex-direction: column; align-items: center; gap: 12px; }
footer.card:hover { transform: none; box-shadow: none; }
footer.card::before, footer.card::after { display: none; }
footer { grid-column: span 6; text-align: center; color: var(--muted); font-size: .8rem; }
.switch { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.switch a {
color: var(--muted); text-decoration: none; font-size: .78rem;
padding: 6px 14px; border-radius: 999px;
border: 1px solid var(--border); background: var(--inset);
transition: transform .16s ease, color .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.switch a:hover { color: var(--text); border-color: #818cf8; transform: translateY(-2px); box-shadow: 0 6px 18px rgba(99,102,241,.25); }
.switch a[aria-current="page"] {
color: #fff; border-color: transparent; background: var(--grad);
box-shadow: 0 6px 20px rgba(99,102,241,.45);
}
/* ===== 响应式 ===== */
@media (max-width: 880px) {
.grid { grid-template-columns: repeat(2, 1fr); }
.hero { grid-column: span 2; grid-row: auto; transform: none !important; }
.sky { grid-column: span 2; grid-row: auto; flex-direction: row; align-items: center; gap: 18px; }
.sky .emoji { font-size: 2.6rem; }
.panel { grid-column: span 2; }
.chip { display: none; }
}
@media (max-width: 540px) {
.grid { grid-template-columns: 1fr; gap: 12px; }
.clockcard, .datecard, footer { grid-column: auto; }
.wrap { padding: 12px 14px 32px; }
header.topbar { padding: 12px 14px; }
form.search kbd { display: none; }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.card:not(.in) { opacity: 1; }
.card.in { animation: none; }
.hero h1, .sky .emoji, .dot, .aurora { animation: none; }
.hero { transform: none !important; }
.card, ul.tiles a, ul.tiles .icon, form.search, form.search button, .switch a, .tbtn, .brand .logo { transition: none; }
}
</style>
<noscript><style>.card { opacity: 1 !important; } .topbar::before { opacity: 1; }</style></noscript>
</head>
<body>
<div class="aurora"></div>
<div class="bg-grid"></div>
<div class="bg-noise"></div>
<div class="orb orb1" id="orb1"></div>
<div class="orb orb2" id="orb2"></div>
<div class="orb orb3" id="orb3"></div>
<div class="glow glow-a" id="glowa"></div>
<div class="glow glow-b" id="glowb"></div>
<header class="topbar" id="topbar">
<a class="brand" href="/"><span class="logo"></span>hyonglin.top</a>
<div class="top-right">
<span class="chip"><span class="dot"></span>服务运行中</span>
<button id="themeBtn" class="tbtn" type="button">☀️</button>
</div>
</header>
<main class="wrap">
<div class="grid">
<!-- Hero:问候 + 标题 + 搜索 -->
<section class="card hero" style="--d:.05s">
<span class="eyebrow" id="greet">个人主页</span>
<h1>hyonglin.top</h1>
<p class="sub">个人服务导航 · <strong>随时可达</strong>,按 <kbd style="font-family:inherit;font-size:.8em;border:1px solid var(--border);border-bottom-width:2px;border-radius:5px;padding:1px 6px;">/</kbd> 快速搜索</p>
<form class="search" action="https://www.bing.com/search" method="get" target="_blank" rel="noopener">
<span class="sicon">🔍</span>
<select name="engine" id="engine" aria-label="搜索引擎">
<option value="bing" selected>Bing</option>
<option value="google">Google</option>
<option value="baidu">百度</option>
<option value="duckduckgo">DuckDuckGo</option>
</select>
<input type="text" name="q" placeholder="输入关键词,回车搜索…" autocomplete="off" aria-label="搜索">
<kbd>/</kbd>
<button type="submit">搜索</button>
</form>
</section>
<!-- 天气 -->
<section class="card sky" style="--d:.12s">
<div class="label">天气</div>
<div class="emoji">☀️</div>
<div class="temp wtxt">获取中…</div>
<div class="wcity"></div>
</section>
<!-- 时间 / 日期 -->
<section class="card clockcard" style="--d:.2s">
<div class="label">时间 · 本地</div>
<div class="value" id="clockval">--:--:--</div>
</section>
<section class="card datecard" style="--d:.26s">
<div class="label">日期</div>
<div class="value" id="dateval">----</div>
</section>
<!-- 自建服务 -->
<section class="card panel s-self" style="--d:.3s">
<h2>📡 自建服务<span class="count">5</span></h2>
<ul class="tiles">
<li><a href="https://note.hyonglin.top"><span class="icon">📝</span><span class="meta"><span class="name">笔记</span><span class="desc">note.hyonglin.top · SilverBullet</span></span><span class="go">↗</span></a></li>
<li><a href="https://git.hyonglin.top"><span class="icon">📦</span><span class="meta"><span class="name">Git 服务</span><span class="desc">git.hyonglin.top · Gitea</span></span><span class="go">↗</span></a></li>
<li><a href="https://ssh.hyonglin.top"><span class="icon">🖥️</span><span class="meta"><span class="name">网页 SSH 终端</span><span class="desc">ssh.hyonglin.top</span></span><span class="go">↗</span></a></li>
<li><a href="https://frp.hyonglin.top"><span class="icon">🔗</span><span class="meta"><span class="name">frp 管理面板</span><span class="desc">frp.hyonglin.top</span></span><span class="go">↗</span></a></li>
<li><a href="/xinquandashboard"><span class="icon">🖼️</span><span class="meta"><span class="name">Xinquan Dashboard</span><span class="desc">hyonglin.top/xinquandashboard</span></span><span class="go">↗</span></a></li>
</ul>
</section>
<!-- AI 助手 -->
<section class="card panel s-ai" style="--d:.36s">
<h2>🤖 AI 助手<span class="count">2</span></h2>
<ul class="tiles">
<li><a href="https://chat.deepseek.com/" target="_blank" rel="noopener"><span class="icon">🤖</span><span class="meta"><span class="name">DeepSeek</span><span class="desc">chat.deepseek.com</span></span><span class="go">↗</span></a></li>
<li><a href="https://www.doubao.com/chat/" target="_blank" rel="noopener"><span class="icon">💬</span><span class="meta"><span class="name">豆包</span><span class="desc">doubao.com</span></span><span class="go">↗</span></a></li>
</ul>
</section>
<!-- 云服务 -->
<section class="card panel s-cloud" style="--d:.42s">
<h2>☁️ 云服务<span class="count">3</span></h2>
<ul class="tiles">
<li><a href="https://dash.cloudflare.com/" target="_blank" rel="noopener"><span class="icon">🌐</span><span class="meta"><span class="name">Cloudflare</span><span class="desc">dash.cloudflare.com</span></span><span class="go">↗</span></a></li>
<li><a href="https://auth.huaweicloud.com/" target="_blank" rel="noopener"><span class="icon">☁️</span><span class="meta"><span class="name">华为云</span><span class="desc">auth.huaweicloud.com</span></span><span class="go">↗</span></a></li>
<li><a href="https://console.cloud.tencent.com/" target="_blank" rel="noopener"><span class="icon">🐧</span><span class="meta"><span class="name">腾讯云</span><span class="desc">console.cloud.tencent.com</span></span><span class="go">↗</span></a></li>
</ul>
</section>
<!-- 常用 -->
<section class="card panel s-util" style="--d:.48s">
<h2>🔧 常用<span class="count">3</span></h2>
<ul class="tiles">
<li><a href="https://wx.mail.qq.com/" target="_blank" rel="noopener"><span class="icon">📧</span><span class="meta"><span class="name">QQ 邮箱</span><span class="desc">wx.mail.qq.com</span></span><span class="go">↗</span></a></li>
<li><a href="https://www.bing.com/" target="_blank" rel="noopener"><span class="icon">🔍</span><span class="meta"><span class="name">Bing 搜索</span><span class="desc">bing.com</span></span><span class="go">↗</span></a></li>
<li><a href="https://www.bilibili.com/" target="_blank" rel="noopener"><span class="icon">📺</span><span class="meta"><span class="name">哔哩哔哩</span><span class="desc">bilibili.com</span></span><span class="go">↗</span></a></li>
</ul>
</section>
<!-- 娱乐 -->
<section class="card panel s-game" style="--d:.54s">
<h2>🎮 娱乐<span class="count">1</span></h2>
<ul class="tiles">
<li class="wide"><a href="/spongebob-1-1.html"><span class="icon">🍍</span><span class="meta"><span class="name">海绵宝宝大冒险 · 比奇堡 1-1</span><span class="desc">横版闯关小游戏 · 方向键移动,空格跳跃,Shift 加速,P 暂停,R 重开</span></span><span class="go">↗</span></a></li>
</ul>
</section>
<footer style="--d:.6s" class="card">
<nav class="switch">
<a href="/terminal/">终端版</a>
<a href="/" aria-current="page">Bento</a>
<a href="/brutal/">Brutal</a>
<a href="/swiss/">Swiss</a>
<a href="/hud/">HUD</a>
</nav>
<span>© <span id="year">2025</span> hyonglin.top</span>
</footer>
</div>
</main>
<script>
/* ===== 主题切换:默认深色,点 ☀️/🌙 切换并记住 ===== */
(function(){
var root = document.documentElement;
var btn = document.getElementById('themeBtn');
if (!btn) return;
var saved = null;
try { saved = localStorage.getItem('theme'); } catch (e) {}
if (saved === 'dark' || saved === 'light') root.setAttribute('data-theme', saved);
function isDark() { return root.getAttribute('data-theme') !== 'light'; }
function label() {
btn.textContent = isDark() ? '☀️' : '🌙';
btn.setAttribute('aria-label', isDark() ? '切换到浅色模式' : '切换到深色模式');
}
btn.addEventListener('click', function(){
var next = isDark() ? 'light' : 'dark';
root.setAttribute('data-theme', next);
try { localStorage.setItem('theme', next); } catch (e) {}
label();
});
label();
})();
/* ===== 顶栏滚动后浮现毛玻璃底 ===== */
(function(){
var bar = document.getElementById('topbar');
if (!bar) return;
function onScroll() { bar.classList.toggle('stuck', window.scrollY > 8); }
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
})();
/* ===== 卡片滚动进入动画(模糊浮现,逐个错峰) ===== */
(function(){
var cards = Array.prototype.slice.call(document.querySelectorAll('.card'));
var reduced = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced || !('IntersectionObserver' in window)) {
cards.forEach(function(c){ c.classList.add('in'); });
return;
}
var io = new IntersectionObserver(function(entries){
entries.forEach(function(en){
if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); }
});
}, { threshold: .08 });
cards.forEach(function(c){ io.observe(c); });
})();
/* ===== 卡内聚光:跟随光标的高光点 ===== */
(function(){
if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
document.querySelectorAll('.card').forEach(function(card){
card.addEventListener('pointermove', function(e){
var r = card.getBoundingClientRect();
card.style.setProperty('--mx', (e.clientX - r.left) + 'px');
card.style.setProperty('--my', (e.clientY - r.top) + 'px');
}, { passive: true });
});
})();
/* ===== Hero 3D 倾斜:随光标轻微透视转动 ===== */
(function(){
if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var hero = document.querySelector('.hero');
if (!hero) return;
hero.addEventListener('pointermove', function(e){
var r = hero.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width - .5;
var py = (e.clientY - r.top) / r.height - .5;
hero.style.setProperty('--ry', (px * 5).toFixed(2) + 'deg');
hero.style.setProperty('--rx', (-py * 4).toFixed(2) + 'deg');
}, { passive: true });
hero.addEventListener('pointerleave', function(){
hero.style.setProperty('--ry', '0deg');
hero.style.setProperty('--rx', '0deg');
});
})();
/* ===== 动态光晕:三团背景光斑缓慢漂移 + 鼠标视差,两团柔光跟随光标(带延迟拖尾) ===== */
(function(){
if (!window.matchMedia || matchMedia('(prefers-reduced-motion: reduce)').matches) return; // 减少动态效果:保持静态
var o1 = document.getElementById('orb1'),
o2 = document.getElementById('orb2'),
o3 = document.getElementById('orb3'),
gA = document.getElementById('glowa'),
gB = document.getElementById('glowb');
if (!o1 || !gA) return;
gA.style.opacity = '1';
gB.style.opacity = '1';
var W = window.innerWidth, H = window.innerHeight;
var tx = W / 2, ty = H / 2; // 光标目标位置
var cx = tx, cy = ty; // 主光晕(快)
var bx = tx, by = ty; // 副光晕(慢,拖尾感)
window.addEventListener('mousemove', function(e){ tx = e.clientX; ty = e.clientY; }, { passive: true });
window.addEventListener('resize', function(){ W = window.innerWidth; H = window.innerHeight; });
var t0 = performance.now();
function loop(now) {
var t = (now - t0) / 1000;
cx += (tx - cx) * .09;
cy += (ty - cy) * .09;
bx += (tx - bx) * .045;
by += (ty - by) * .045;
var nx = cx / W - .5, ny = cy / H - .5; // 归一化鼠标位置(-0.5 ~ 0.5)
o1.style.transform = 'translate(' + (Math.sin(t * .32) * 34 + nx * 70).toFixed(1) + 'px,' + (Math.cos(t * .26) * 28 + ny * 70).toFixed(1) + 'px)';
o2.style.transform = 'translate(' + (Math.sin(t * .24 + 2) * 40 - nx * 90).toFixed(1) + 'px,' + (Math.cos(t * .30 + 1) * 34 - ny * 90).toFixed(1) + 'px)';
o3.style.transform = 'translate(' + (Math.sin(t * .28 + 4) * 36 + nx * 50).toFixed(1) + 'px,' + (Math.cos(t * .22 + 3) * 30 + ny * 50).toFixed(1) + 'px)';
gA.style.transform = 'translate(' + (cx - 280).toFixed(1) + 'px,' + (cy - 280).toFixed(1) + 'px)';
gB.style.transform = 'translate(' + (bx - 210).toFixed(1) + 'px,' + (by - 210).toFixed(1) + 'px)';
requestAnimationFrame(loop);
}
loop(performance.now()); // 先同步画一帧,避免光晕从屏幕外滑入
requestAnimationFrame(loop);
})();
/* ===== 搜索引擎选择 ===== */
(function(){
var sel = document.getElementById('engine');
if (!sel) return;
var ENGINES = {
bing: 'https://www.bing.com/search?q=',
google: 'https://www.google.com/search?q=',
baidu: 'https://www.baidu.com/s?wd=',
duckduckgo: 'https://duckduckgo.com/?q='
};
var form = sel.closest('form');
function apply() { form.action = ENGINES[sel.value] || ENGINES.bing; }
sel.addEventListener('change', apply);
apply();
})();
/* ===== “/” 快捷键聚焦搜索 ===== */
(function(){
var input = document.querySelector('form.search input');
if (!input) return;
document.addEventListener('keydown', function(e){
var tag = (document.activeElement && document.activeElement.tagName) || '';
if (e.key === '/' && tag !== 'INPUT' && tag !== 'SELECT' && tag !== 'TEXTAREA') {
e.preventDefault();
input.focus();
}
});
})();
/* ===== 问候语 ===== */
(function(){
var el = document.getElementById('greet');
if (!el) return;
var h = new Date().getHours();
var g = h < 5 ? '夜深了' : h < 11 ? '早上好' : h < 13 ? '中午好' : h < 18 ? '下午好' : h < 23 ? '晚上好' : '夜深了';
el.textContent = g + ' · hyonglin';
})();
/* ===== 日期 + 时钟(秒数弱化显示) ===== */
var week = ['日','一','二','三','四','五','六'];
function pad2(n) { return (n < 10 ? '0' : '') + n; }
function updateClock() {
var now = new Date();
var c = document.getElementById('clockval');
if (c) c.innerHTML = pad2(now.getHours()) + ':' + pad2(now.getMinutes()) + '<span class="sec">:' + pad2(now.getSeconds()) + '</span>';
var d = document.getElementById('dateval');
if (d) d.innerHTML = now.getFullYear() + '-' + pad2(now.getMonth()+1) + '-' + pad2(now.getDate()) + ' <span class="wk">周' + week[now.getDay()] + '</span>';
var y = document.getElementById('year');
if (y) y.textContent = now.getFullYear();
}
setInterval(updateClock, 1000);
updateClock();
/* ===== 天气:GPS 定位 + Open-Meteo(不依赖 IP) ===== */
var emojiBox = function(){ return document.querySelector('.sky .emoji'); };
var tempBox = function(){ return document.querySelector('.wtxt'); };
var cityBox = function(){ return document.querySelector('.wcity'); };
var WMO = {
0:'☀️',1:'🌤',2:'⛅',3:'☁️',45:'🌫',48:'🌫',
51:'🌦',53:'🌦',55:'🌦',56:'🌨',57:'🌨',
61:'🌧',63:'🌧',65:'🌧',66:'🌨',67:'🌨',
71:'🌨',73:'🌨',75:'🌨',77:'🌨',80:'🌦',81:'🌦',82:'🌧',
85:'🌨',86:'🌨',95:'⛈',96:'⛈',99:'⛈'
};
function wmo(w) { return WMO[w] || '🌡️'; }
function setWeather(temp, wcode, city) {
if (tempBox()) tempBox().textContent = temp + '°C';
if (emojiBox()) emojiBox().textContent = wmo(wcode);
if (city && cityBox()) cityBox().textContent = city;
}
function getWeather(lat, lon, city) {
var url = 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon +
'&current_weather=true&timezone=auto';
fetch(url).then(function(r){ return r.json(); }).then(function(d){
var cw = d.current_weather || {};
setWeather(Math.round(cw.temperature), cw.weathercode, city);
}).catch(function(){ tempBox() && (tempBox().textContent = '天气获取失败'); });
}
// 优先浏览器 GPS 定位;失败则 ipwho.is 按 IP 定位;再失败显示默认城市(北京)
var DEFAULT_LOC = { lat: 39.9042, lon: 116.4074, city: '北京' };
function byIP() {
fetch('https://ipwho.is/').then(function(r){ return r.json(); }).then(function(d){
if (d && d.latitude && d.longitude) getWeather(d.latitude, d.longitude, d.city || '');
else { getWeather(DEFAULT_LOC.lat, DEFAULT_LOC.lon, DEFAULT_LOC.city); }
}).catch(function(){ getWeather(DEFAULT_LOC.lat, DEFAULT_LOC.lon, DEFAULT_LOC.city); });
}
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(pos){
getWeather(pos.coords.latitude, pos.coords.longitude, '');
}, byIP, { timeout: 8000 });
} else {
byIP();
}
</script>
</body>
</html>