调研收官产物入库:主报告(架构 C''=Astro SSG+gitea 双写作流、 Artalk 评论、分片 webfont、双语预译管线、多端适配规格、 Phase 0-5 实施规划)+ 五份侦察原稿 + 可交互单文件样板 (三路线×明暗×五页型,命令面板/终端/热力图/masonry)。 Co-authored-by: GLM-5.3
1051 lines
67 KiB
HTML
1051 lines
67 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN" data-route="c" data-page="home" data-scheme="light">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>wait4x · 视觉样板</title>
|
||
<script>if(matchMedia('(prefers-color-scheme: dark)').matches)document.documentElement.dataset.scheme='dark';</script>
|
||
<style>
|
||
/* ============ 基底与常量 ============ */
|
||
*{box-sizing:border-box;margin:0}
|
||
:root{--tbh:54px;--h0:#ebedf0;--h1:#9be9a8;--h2:#40c463;--h3:#30a14e;--h4:#216e39}
|
||
html{
|
||
color-scheme:light;
|
||
--serif:Georgia,"Iowan Old Style","Songti SC",STSong,"Noto Serif CJK SC","Source Han Serif SC",SimSun,serif;
|
||
--sans:system-ui,-apple-system,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC",sans-serif;
|
||
--mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,"Liberation Mono",monospace;
|
||
--ease:cubic-bezier(.19,1,.22,1);
|
||
--dur:.35s;
|
||
--stage:#f4f4f2;
|
||
scroll-padding-top:calc(var(--tbh) + 20px);
|
||
}
|
||
html[data-scheme="dark"]{color-scheme:dark;--stage:#0b0d10;--h0:#22272e;--h1:#0e4429;--h2:#006d32;--h3:#26a641;--h4:#39d353}
|
||
::selection{background:color-mix(in srgb,var(--accent) 26%,transparent)}
|
||
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
|
||
body{
|
||
background:var(--bg);color:var(--text);
|
||
font-family:var(--font-ui);font-size:16px;line-height:1.7;
|
||
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
|
||
padding-top:var(--tbh);
|
||
}
|
||
a{color:inherit;text-decoration:none}
|
||
button{font:inherit}
|
||
img,svg{display:block}
|
||
|
||
/* ============ 路线甲 · 纸墨书卷 ============ */
|
||
html[data-route="a"]{
|
||
--font-ui:var(--serif);--font-essay:var(--serif);--font-tech:var(--serif);
|
||
--bg:#f6f5f3;--surface:#fff;--text:#2c2620;--title:#1d1912;
|
||
--muted:#7d7466;--faint:#a89e8e;
|
||
--border:#e6e1d6;--border-strong:#d4ccbc;
|
||
--accent:#009900;
|
||
--measure:648px;
|
||
--sheet:#fff;--sheet-line:var(--border);--sheet-pad:36px;--sheet-radius:8px;--sheet-ink:#2c2620;
|
||
--inline-bg:#efede7;--quote-line:#c9c0ad;
|
||
}
|
||
html[data-route="a"][data-scheme="dark"]{
|
||
--bg:#1f1c17;--surface:#262119;--text:#d8d1c0;--title:#e8e2d5;
|
||
--muted:#9c927e;--faint:#6f6656;
|
||
--border:#332d22;--border-strong:#453d2d;
|
||
--accent:#7fb069;
|
||
--sheet:#262119;--sheet-line:#3a3325;--sheet-ink:#d8d1c0;
|
||
--inline-bg:#2d2820;--quote-line:#5a5140;
|
||
}
|
||
/* ============ 路线乙 · 极简灰阶 ============ */
|
||
html[data-route="b"]{
|
||
--font-ui:var(--sans);--font-essay:var(--sans);--font-tech:var(--sans);
|
||
--bg:#fff;--surface:#fff;--text:#333;--title:#111;
|
||
--muted:#666;--faint:#999;
|
||
--border:#e8e8e8;--border-strong:#d4d4d4;
|
||
--accent:#3454d1;
|
||
--measure:680px;
|
||
--sheet:transparent;--sheet-line:transparent;--sheet-pad:0;--sheet-radius:0;--sheet-ink:inherit;
|
||
--inline-bg:#f2f2f2;--quote-line:#ddd;
|
||
}
|
||
html[data-route="b"][data-scheme="dark"]{
|
||
--bg:#282c35;--surface:#282c35;--text:#c9ccd4;--title:#f5f5f7;
|
||
--muted:#8b8f98;--faint:#5c6066;
|
||
--border:#363b46;--border-strong:#454b58;
|
||
--accent:#8ea2ff;
|
||
--inline-bg:#31353f;--quote-line:#4a505c;
|
||
}
|
||
/* ============ 路线丙 · 灰阶骨架 × 分轨正文 ============ */
|
||
html[data-route="c"]{
|
||
--font-ui:var(--sans);--font-essay:var(--serif);--font-tech:var(--sans);
|
||
--bg:#fff;--surface:#fff;--text:#333;--title:#111;
|
||
--muted:#666;--faint:#999;
|
||
--border:#e8e8e8;--border-strong:#d4d4d4;
|
||
--accent:#3454d1;
|
||
--measure:704px;
|
||
--sheet:#f6f5f3;--sheet-line:#e6e1d6;--sheet-pad:44px;--sheet-radius:14px;--sheet-ink:#2c2620;
|
||
--inline-bg:#f1f2f4;--quote-line:#d0d0d0;
|
||
}
|
||
html[data-route="c"][data-scheme="dark"]{
|
||
--bg:#191d23;--surface:#191d23;--text:#c6cbd4;--title:#eef1f5;
|
||
--muted:#8b8f98;--faint:#565b66;
|
||
--border:#2b303a;--border-strong:#3d434f;
|
||
--accent:#8ea2ff;
|
||
--sheet:#201d18;--sheet-line:#332e24;--sheet-ink:#d8d1c0;
|
||
--inline-bg:#2a2e36;--quote-line:#4a4438;
|
||
}
|
||
|
||
/* ============ 预览工具条(非博客 UI) ============ */
|
||
.tb{
|
||
position:fixed;top:0;left:0;right:0;z-index:100;
|
||
display:flex;align-items:center;gap:10px;flex-wrap:nowrap;overflow-x:auto;
|
||
padding:9px 16px;height:var(--tbh);
|
||
background:#1a1d23f2;backdrop-filter:blur(8px);
|
||
border-bottom:1px solid #ffffff14;color:#a7aeb9;
|
||
scrollbar-width:none;
|
||
}
|
||
.tb::-webkit-scrollbar{display:none}
|
||
.tb-brand{flex:none;font-size:12px;color:#8a93a3;font-family:var(--sans);white-space:nowrap}
|
||
.tb-brand b{color:#e6e9ee;font-weight:600}
|
||
.tb-sep{flex:none;width:1px;height:16px;background:#ffffff22}
|
||
.tb-group{display:flex;gap:2px;flex:none}
|
||
.tb-seg{position:relative}
|
||
.tb-seg input{position:absolute;opacity:0;pointer-events:none}
|
||
.tb-seg span{
|
||
display:inline-block;padding:4px 10px;border-radius:7px;
|
||
font-size:12px;font-family:var(--sans);white-space:nowrap;cursor:pointer;
|
||
color:#a7aeb9;user-select:none;
|
||
}
|
||
.tb-seg span:hover{color:#e6e9ee}
|
||
.tb-seg input:checked + span{background:#2c313b;color:#fff}
|
||
.tb-seg input:focus-visible + span{outline:1px solid #9db4ff}
|
||
.tb-seg .badge{
|
||
font-style:normal;font-size:10px;color:#f5c97b;border:1px solid #f5c97b55;
|
||
border-radius:5px;padding:0 5px;margin-left:6px;vertical-align:1px;
|
||
}
|
||
.tb-btn{
|
||
flex:none;display:inline-flex;align-items:center;justify-content:center;
|
||
width:28px;height:28px;border-radius:7px;border:none;cursor:pointer;
|
||
background:transparent;color:#a7aeb9;
|
||
}
|
||
.tb-btn:hover{background:#2c313b;color:#fff}
|
||
.tb-right{flex:none;display:flex;gap:4px;margin-left:auto}
|
||
.tb-pal{width:auto;gap:5px;padding:0 9px}
|
||
.tb-pal span{font-family:var(--mono);font-size:10.5px;letter-spacing:.04em}
|
||
.ic-sun{display:none}
|
||
html[data-scheme="dark"] .ic-sun{display:block}
|
||
html[data-scheme="dark"] .ic-moon{display:none}
|
||
|
||
/* ============ 阅读进度条(仅技术文章页) ============ */
|
||
#pbar{
|
||
position:fixed;top:0;left:0;height:2px;width:0;z-index:200;
|
||
background:var(--accent);display:none;
|
||
}
|
||
html[data-page="tech"] #pbar{display:block}
|
||
|
||
/* ============ 站点骨架 ============ */
|
||
.container{max-width:var(--measure);margin:0 auto;padding:0 20px}
|
||
.site-head{border-bottom:1px solid var(--border)}
|
||
.site-head-row{display:flex;align-items:center;justify-content:space-between;height:58px}
|
||
.site-name{font-size:17px;font-weight:700;color:var(--title)}
|
||
html[data-route="b"] .site-name{font-weight:700}
|
||
.nav{display:flex;align-items:center;gap:18px}
|
||
.nav-item{
|
||
display:inline-flex;align-items:center;gap:6px;
|
||
font-size:14px;color:var(--muted);cursor:pointer;background:none;border:none;padding:0;
|
||
}
|
||
.nav-item:hover{color:var(--title)}
|
||
html[data-route="b"] .nav-t{display:none}
|
||
html:not([data-route="b"]) .nav-item:not(.nav-icon) svg{display:none}
|
||
html:has(input#p-home:checked) .nav-item[for="p-home"],
|
||
html:has(input#p-gallery:checked) .nav-item[for="p-gallery"]{color:var(--title)}
|
||
.site-foot{margin-top:80px;border-top:1px solid var(--border)}
|
||
.site-foot .container{
|
||
padding-top:22px;padding-bottom:40px;
|
||
font-size:12.5px;color:var(--faint);
|
||
}
|
||
.site-foot a{color:var(--faint)}
|
||
.site-foot a:hover{color:var(--accent)}
|
||
/* 页脚构建透明:mono 微文案,hash 暗示链到 gitea commit */
|
||
.build{margin-top:8px;font-family:var(--mono);font-size:11px;letter-spacing:.02em}
|
||
.build a{text-decoration:underline;text-decoration-style:dashed;text-underline-offset:3px}
|
||
.build a:hover{color:var(--accent)}
|
||
.foot-term{
|
||
margin-left:10px;display:inline-flex;align-items:center;justify-content:center;
|
||
background:none;border:none;color:var(--faint);cursor:pointer;
|
||
padding:2px;border-radius:5px;vertical-align:-2px;
|
||
}
|
||
.foot-term:hover{color:var(--accent)}
|
||
|
||
/* ============ 页面切换 ============ */
|
||
.view{display:none;padding-bottom:24px}
|
||
html[data-page="home"] .view-home,
|
||
html[data-page="essay"] .view-essay,
|
||
html[data-page="tech"] .view-tech,
|
||
html[data-page="gallery"] .view-gallery,
|
||
html[data-page="moments"] .view-moments{display:block}
|
||
html[data-page="tech"] .view-tech .container{max-width:calc(var(--measure) + 200px)}
|
||
html[data-page="gallery"] .view-gallery .container{max-width:calc(var(--measure) + 176px)}
|
||
|
||
/* ============ 首页 ============ */
|
||
.hero{display:flex;align-items:center;gap:18px;margin:44px 0 40px}
|
||
.hero-avatar{flex:none;border-radius:14px;box-shadow:0 1px 2px #0000001a}
|
||
.hero-name{font-size:21px;font-weight:700;color:var(--title)}
|
||
.hero-bio{font-size:15px;color:var(--muted);margin:4px 0 12px}
|
||
.socials{display:flex;align-items:center;gap:14px}
|
||
.socials a{color:var(--muted)}
|
||
.socials a:hover{color:var(--accent)}
|
||
.year{font-size:15px;font-weight:600;color:var(--muted);margin:6px 0 4px}
|
||
.track{
|
||
display:flex;align-items:center;gap:8px;
|
||
margin:22px 0 4px;font-size:12.5px;color:var(--faint);
|
||
}
|
||
.track::before{content:"";width:7px;height:7px;border-radius:2px;background:var(--border-strong)}
|
||
.row{
|
||
display:flex;align-items:baseline;gap:12px;
|
||
padding:9px 0;cursor:pointer;
|
||
}
|
||
.row-title{flex:1;min-width:0;font-size:16px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.row:hover .row-title{color:var(--accent)}
|
||
.row-date{flex:none;color:var(--faint);font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap}
|
||
.emo{
|
||
display:none;width:10px;height:10px;border-radius:3px;
|
||
background:var(--emo,#999);flex:none;align-self:center;
|
||
}
|
||
html[data-route="c"] .emo{display:inline-block}
|
||
.tag{
|
||
flex:none;font-family:var(--mono);font-size:12px;color:var(--muted);
|
||
background:var(--inline-bg);border-radius:5px;padding:1px 7px;white-space:nowrap;
|
||
}
|
||
/* 最新影像入口:仅乙 / 丙 */
|
||
.latest{display:none;margin-top:44px}
|
||
html[data-route="b"] .latest,
|
||
html[data-route="c"] .latest{display:block}
|
||
.latest-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
|
||
.latest-head .lab{font-size:12.5px;color:var(--faint)}
|
||
.more{font-size:13px;color:var(--accent);cursor:pointer}
|
||
.more:hover{color:var(--title)}
|
||
.thumbs{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:12px}
|
||
.thumbs figure{aspect-ratio:1/1;border-radius:8px;overflow:hidden;cursor:pointer}
|
||
.thumbs svg{width:100%;height:100%}
|
||
|
||
/* ============ 首页 · 开源活动(贡献热力图 + 项目卡) ============ */
|
||
.oss{margin-top:44px}
|
||
#heat{
|
||
display:grid;grid-template-rows:repeat(7,10px);grid-auto-flow:column;grid-auto-columns:10px;gap:3px;
|
||
margin-top:12px;width:max-content;max-width:100%;overflow-x:auto;padding-bottom:2px;
|
||
}
|
||
#heat i,.heat-legend i{border-radius:2.5px}
|
||
.h0{background:var(--h0)}.h1{background:var(--h1)}.h2{background:var(--h2)}.h3{background:var(--h3)}.h4{background:var(--h4)}
|
||
.heat-meta{
|
||
display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;
|
||
margin-top:10px;font-size:12px;color:var(--faint);
|
||
}
|
||
.heat-legend{display:inline-flex;align-items:center;gap:3px;font-family:var(--mono)}
|
||
.heat-legend i{width:9px;height:9px;margin:0 1px}
|
||
.repos{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:16px}
|
||
.repo{
|
||
display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;
|
||
border:1px solid var(--border);border-radius:10px;padding:12px 14px;
|
||
font-size:13px;color:var(--muted);cursor:pointer;
|
||
}
|
||
.repo code{font-family:var(--mono);font-size:13px;color:var(--title)}
|
||
.repo-meta{width:100%;display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:12px;margin-top:6px}
|
||
.repo-meta i{width:8px;height:8px;border-radius:50%;background:var(--lc,#999);flex:none}
|
||
.repo-meta b{margin-left:auto;font-weight:400;font-variant-numeric:tabular-nums}
|
||
|
||
/* ============ 正文排版(三路线共用参数) ============ */
|
||
.prose{max-width:min(720px,var(--measure));overflow-wrap:break-word}
|
||
.prose p{margin:0 0 .9em;text-indent:0}
|
||
.essay .prose{font-family:var(--font-essay);font-size:var(--prose-size,20px);line-height:1.75}
|
||
.tech .prose{font-family:var(--font-tech);font-size:19px;line-height:1.7}
|
||
html[data-route="a"] .prose{text-align:justify}
|
||
.post-title{
|
||
font-size:27px;line-height:1.3;letter-spacing:0;text-wrap:balance;
|
||
color:var(--title);font-weight:700;
|
||
}
|
||
.prose h2,.prose h3{letter-spacing:0;text-wrap:balance;color:var(--title);line-height:1.3}
|
||
.prose h2{font-size:22px;margin:1.8em 0 .6em;scroll-margin-top:calc(var(--tbh) + 24px)}
|
||
.prose h3{font-size:19px;margin:1.5em 0 .5em;scroll-margin-top:calc(var(--tbh) + 24px)}
|
||
html[data-route="b"] .post-title{font-weight:900}
|
||
html[data-route="b"] .prose h2,
|
||
html[data-route="b"] .prose h3{font-weight:700}
|
||
/* 引文 A:长书摘(1px 墨线左边框 + muted) */
|
||
.prose blockquote.pull{
|
||
margin:1.6em 0;padding:.1em 0 .1em 1.5em;
|
||
border-left:1px solid var(--quote-line);color:var(--muted);
|
||
font-size:inherit;line-height:inherit;
|
||
}
|
||
.prose blockquote.pull p{margin:0}
|
||
/* 引文 D:金句(62px 巨型左引号水印 + 缩进) */
|
||
.quote-hero{position:relative;margin:1.9em 0 .4em;padding-left:2.1em}
|
||
.quote-hero::before{
|
||
content:"“";position:absolute;left:0;top:-26px;
|
||
font-family:var(--serif);font-size:62px;line-height:1;
|
||
color:var(--text);opacity:.12;pointer-events:none;
|
||
}
|
||
.quote-hero p{margin:0;font-size:inherit;line-height:inherit;color:var(--title)}
|
||
.quote-src{font-size:.875rem;color:var(--faint);margin:-.3em 0 1.8em}
|
||
/* 内联代码 */
|
||
.prose code:not(pre code){
|
||
font-family:var(--mono);font-size:.875em;
|
||
background:var(--inline-bg);border-radius:4px;padding:.15em .4em;
|
||
}
|
||
/* 终端风代码块:黑底、圆点、$ 提示符、手写语法色 */
|
||
.term{margin:1.5em 0;border-radius:10px;overflow:hidden;background:#0d1117;border:1px solid #ffffff12}
|
||
.term-bar{
|
||
display:flex;align-items:center;gap:6px;
|
||
padding:9px 12px;border-bottom:1px solid #ffffff0d;
|
||
}
|
||
.term-bar .dot{width:10px;height:10px;border-radius:50%}
|
||
.term-bar .r{background:#ff5f56}.term-bar .y{background:#ffbd2e}.term-bar .g{background:#27c93f}
|
||
.term-title{margin-left:6px;font-family:var(--mono);font-size:11px;color:#8b949e}
|
||
.term pre{
|
||
margin:0;padding:14px 18px;overflow-x:auto;
|
||
font-family:var(--mono);font-size:16px;line-height:1.6;color:#c9d1d9;
|
||
white-space:pre;
|
||
}
|
||
.term .cm{color:#5c6370}
|
||
.term .str{color:#98c379}
|
||
.term .cmd{color:#61afef}
|
||
.term .kw{color:#c678dd}
|
||
.term .pr{color:#61afef;font-weight:700;user-select:none}
|
||
|
||
/* ============ 文章页 · 书评 ============ */
|
||
.post{margin-top:36px}
|
||
.sheet{
|
||
background:var(--sheet);color:var(--sheet-ink,var(--text));
|
||
border:1px solid var(--sheet-line);border-radius:var(--sheet-radius);
|
||
padding:var(--sheet-pad);
|
||
}
|
||
.post-cat{font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
|
||
.post-head .post-title{margin-top:10px}
|
||
.post-meta{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:12px;font-size:14px;color:var(--muted)}
|
||
.post-head + .prose{margin-top:28px}
|
||
/* 上一篇 / 下一篇 */
|
||
.pn{display:flex;justify-content:space-between;gap:16px;margin-top:36px}
|
||
.pn-item{display:flex;flex-direction:column;gap:2px;min-width:0}
|
||
.pn-item.right{text-align:right;align-items:flex-end}
|
||
.pn-item span{font-size:12px;color:var(--faint)}
|
||
.pn-item strong{
|
||
font-size:15px;font-weight:600;color:var(--title);
|
||
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;cursor:pointer;
|
||
}
|
||
.pn-item:hover strong{color:var(--accent)}
|
||
/* 评论区占位 */
|
||
.comments{
|
||
margin-top:28px;border:1px dashed var(--border-strong);border-radius:10px;
|
||
padding:30px 20px;text-align:center;color:var(--faint);font-size:13px;
|
||
}
|
||
.comments b{display:block;font-weight:400;color:var(--muted);margin-bottom:4px}
|
||
/* 字号 dock:仅书评页出现 */
|
||
.fsdock{
|
||
position:fixed;right:18px;top:calc(var(--tbh) + 14px);z-index:60;
|
||
display:none;flex-direction:column;gap:6px;
|
||
}
|
||
html[data-page="essay"] .fsdock{display:flex}
|
||
.fsdock button{
|
||
width:38px;height:30px;border:1px solid var(--border);border-radius:8px;
|
||
background:var(--surface);color:var(--muted);font-family:var(--mono);
|
||
font-size:11px;cursor:pointer;
|
||
}
|
||
.fsdock button:hover{color:var(--title);border-color:var(--border-strong)}
|
||
|
||
/* ============ 文章页 · 技术 ============ */
|
||
.tech-grid{display:grid;grid-template-columns:minmax(0,1fr) 172px;gap:56px;align-items:start;margin-top:36px}
|
||
.toc{
|
||
position:sticky;top:calc(var(--tbh) + 24px);
|
||
font-size:13px;padding-left:16px;border-left:1px solid var(--border);
|
||
}
|
||
.toc-h{font-weight:600;color:var(--title);font-size:13px;margin-bottom:6px}
|
||
.toc a{display:block;padding:4px 0;color:var(--muted);cursor:pointer}
|
||
.toc a:hover{color:var(--accent)}
|
||
.toc a.on{color:var(--accent);font-weight:600;box-shadow:inset 2px 0 0 var(--accent);padding-left:10px;margin-left:-11px}
|
||
.toc .toc-sub{padding-left:14px}
|
||
.toc a.on.toc-sub{padding-left:24px}
|
||
.tech .post-meta{margin-top:12px}
|
||
|
||
/* ============ 图库(masonry:格高由照片宽高比驱动) ============ */
|
||
.g-title{font-size:20px;font-weight:700;color:var(--title);margin-top:40px}
|
||
.g-filters{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 20px}
|
||
.chip{
|
||
padding:5px 13px;border-radius:999px;border:1px solid var(--border);
|
||
background:transparent;color:var(--muted);font-size:13px;cursor:pointer;
|
||
}
|
||
.chip:hover{border-color:var(--border-strong);color:var(--title)}
|
||
.chip.on{color:var(--title);border-color:var(--border-strong);font-weight:600}
|
||
.stage{background:var(--stage);padding:14px;border-radius:14px}
|
||
.wall{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:8px;grid-auto-flow:dense;gap:14px}
|
||
.shot{position:relative;aspect-ratio:var(--ar,4/3);border-radius:10px;overflow:hidden;cursor:zoom-in}
|
||
.shot svg{width:100%;height:100%}
|
||
.exif{
|
||
position:absolute;left:0;right:0;bottom:0;
|
||
display:flex;flex-direction:column;gap:2px;
|
||
padding:28px 12px 10px;
|
||
background:linear-gradient(180deg,transparent,#080a0e9e);
|
||
color:#fff;opacity:0;transform:translateY(6px);pointer-events:none;
|
||
}
|
||
.exif strong{font-size:13px;font-weight:600}
|
||
.exif span{font-size:11.5px;opacity:.85;font-variant-numeric:tabular-nums}
|
||
.shot:hover .exif,.shot:focus-within .exif{opacity:1;transform:none}
|
||
.g-note{margin-top:14px;font-size:12.5px;color:var(--faint)}
|
||
|
||
/* ============ 瞬间 · 时间线 ============ */
|
||
.tl{list-style:none;margin:26px 0 0;padding:0}
|
||
.tl-item{
|
||
position:relative;margin:0 0 20px 56px;padding-left:24px;
|
||
border-left:1px solid var(--border-strong);
|
||
}
|
||
.tl-item:last-child{margin-bottom:0}
|
||
.tl-date{
|
||
position:absolute;left:-60px;top:3px;width:44px;text-align:right;
|
||
font-family:var(--mono);font-size:12.5px;color:var(--faint);font-variant-numeric:tabular-nums;
|
||
}
|
||
.tl-item::after{
|
||
content:"";position:absolute;left:-4.5px;top:7px;width:8px;height:8px;border-radius:50%;
|
||
background:var(--accent);box-shadow:0 0 0 3px var(--bg);
|
||
}
|
||
.mom-card{border:1px solid var(--border);border-radius:12px;padding:14px 16px;background:var(--surface)}
|
||
.mom-card p{font-size:15px;color:var(--text);margin:0 0 8px}
|
||
.mom-card p:last-of-type{margin-bottom:0}
|
||
.mom-fig{border-radius:8px;overflow:hidden;margin:0 0 10px;max-width:420px}
|
||
.mom-fig.vt{max-width:230px}
|
||
.mom-fig svg{width:100%;height:auto}
|
||
.mom-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
|
||
|
||
/* ============ 浮层公共 + 命令面板 ============ */
|
||
dialog{border:none;background:var(--surface);color:var(--text);padding:0}
|
||
dialog::backdrop{background:#0b0d1066;backdrop-filter:blur(2px)}
|
||
#palette{
|
||
width:min(560px,calc(100vw - 40px));margin:13vh auto auto;
|
||
border:1px solid var(--border-strong);border-radius:14px;
|
||
box-shadow:0 24px 60px #00000040;overflow:hidden;
|
||
}
|
||
#pq{
|
||
width:100%;border:none;outline:none;background:transparent;
|
||
padding:15px 18px;font:inherit;font-size:15px;color:var(--title);
|
||
border-bottom:1px solid var(--border);
|
||
}
|
||
#pr{max-height:320px;overflow:auto;padding:6px}
|
||
#pr button{
|
||
display:flex;align-items:center;gap:10px;width:100%;text-align:left;
|
||
border:none;background:none;font:inherit;font-size:14px;color:var(--text);
|
||
padding:9px 12px;border-radius:8px;cursor:pointer;
|
||
}
|
||
#pr button em{margin-left:auto;font-style:normal;font-family:var(--mono);font-size:11px;color:var(--faint)}
|
||
#pr button.on{background:var(--inline-bg);color:var(--title);box-shadow:inset 2px 0 0 var(--accent)}
|
||
.pal-none{padding:18px;text-align:center;color:var(--faint);font-size:13px}
|
||
.pal-hint{
|
||
display:flex;gap:16px;padding:10px 16px;border-top:1px solid var(--border);
|
||
font-size:11.5px;color:var(--faint);font-family:var(--mono);
|
||
}
|
||
|
||
/* ============ 快捷键帮助 ============ */
|
||
#keys{width:min(430px,calc(100vw - 40px));margin:auto;border:1px solid var(--border-strong);border-radius:14px;padding:22px 24px}
|
||
.kh{font-size:15px;font-weight:700;color:var(--title);margin-bottom:14px}
|
||
.klist{display:grid;grid-template-columns:auto 1fr;gap:9px 18px;align-items:center;margin:0}
|
||
.klist dt{white-space:nowrap}
|
||
.klist dd{margin:0;font-size:13px;color:var(--muted)}
|
||
kbd{
|
||
font-family:var(--mono);font-size:11.5px;color:var(--title);
|
||
background:var(--inline-bg);border:1px solid var(--border-strong);border-bottom-width:2px;
|
||
border-radius:5px;padding:1px 6px;
|
||
}
|
||
.kn{margin-top:14px;font-size:12px;color:var(--faint)}
|
||
|
||
/* ============ 终端彩蛋 ============ */
|
||
#term{inset:0;width:100vw;height:100vh;max-width:none;max-height:none;margin:0;border-radius:0;background:#0d1117;color:#7ee787}
|
||
#term::backdrop{background:#0d1117}
|
||
.tbar{display:flex;align-items:center;gap:6px;padding:10px 14px;border-bottom:1px solid #ffffff14}
|
||
.tbar .dot{width:10px;height:10px;border-radius:50%}
|
||
.tbar .r{background:#ff5f56}.tbar .y{background:#ffbd2e}.tbar .g{background:#27c93f}
|
||
.tbar .ttitle{margin-left:6px;font-family:var(--mono);font-size:11px;color:#8b949e}
|
||
#tclose{
|
||
margin-left:auto;width:26px;height:26px;border-radius:6px;border:none;cursor:pointer;
|
||
background:none;color:#8b949e;display:grid;place-items:center;
|
||
}
|
||
#tclose:hover{background:#ffffff14;color:#fff}
|
||
#tout{
|
||
padding:16px 18px 8px;font-family:var(--mono);font-size:14px;line-height:1.65;color:#7ee787;
|
||
max-height:calc(100vh - 130px);overflow:auto;white-space:pre-wrap;
|
||
}
|
||
#tout .tc{color:#d29922}
|
||
#tout .te{color:#f85149}
|
||
#tform{display:flex;gap:8px;padding:10px 18px 18px;font-family:var(--mono);font-size:14px}
|
||
.tprompt{color:#d29922;white-space:nowrap;user-select:none}
|
||
#tin{flex:1;min-width:0;background:transparent;border:none;outline:none;color:#7ee787;font:inherit;caret-color:#7ee787}
|
||
#tin:focus-visible{outline:none}
|
||
|
||
/* ============ 灯箱 ============ */
|
||
#lb{inset:0;width:100vw;height:100vh;max-width:none;max-height:none;margin:0;border-radius:0;background:transparent;display:flex;flex-direction:column}
|
||
#lb::backdrop{background:#000000f2}
|
||
#lbimg{flex:1;min-height:0;display:grid;place-items:center;padding:38px 20px 8px}
|
||
#lbimg svg{height:auto;max-height:100%;border-radius:6px}
|
||
.lbbottom{
|
||
display:flex;align-items:center;gap:16px;padding:12px 22px 20px;
|
||
color:#c9d1d9;font-family:var(--mono);font-size:12.5px;
|
||
}
|
||
.lbbottom strong{color:#fff;font-size:13px;font-weight:600;margin-right:10px;font-family:var(--sans)}
|
||
.lbbottom span{opacity:.82;font-variant-numeric:tabular-nums}
|
||
#lbexif{flex:1;min-width:0;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.lbnav{
|
||
flex:none;width:38px;height:38px;border-radius:10px;cursor:pointer;
|
||
border:1px solid #ffffff2e;background:#ffffff0f;color:#e6e9ee;
|
||
display:grid;place-items:center;
|
||
}
|
||
.lbnav:hover{background:#ffffff1f}
|
||
#lbclose{position:absolute;top:16px;right:18px}
|
||
|
||
/* ============ 动效纪律(唯一缓动 × 0.35s,全部包进 no-preference) ============ */
|
||
@media (prefers-reduced-motion: no-preference){
|
||
html{scroll-behavior:smooth}
|
||
.hero,.feed > *,.oss,.latest{animation:rise var(--dur) var(--ease) both;animation-delay:calc(var(--i,0) * 45ms)}
|
||
.view{animation:viewin var(--dur) var(--ease) both}
|
||
dialog[open]{animation:pop .25s var(--ease)}
|
||
.row-title,.row-date,.chip,.toc a,.nav-item,.socials a,.more,.pn-item strong,
|
||
.repo,#pr button,.fsdock button,.lbnav,.foot-term,
|
||
.tb-seg span,.tb-btn,.shot svg,.exif,.site-foot a{
|
||
transition:
|
||
color var(--dur) var(--ease),
|
||
background-color var(--dur) var(--ease),
|
||
border-color var(--dur) var(--ease),
|
||
opacity var(--dur) var(--ease),
|
||
transform var(--dur) var(--ease);
|
||
}
|
||
}
|
||
@keyframes rise{from{opacity:0;transform:translateY(12px)}}
|
||
@keyframes viewin{from{opacity:0;transform:translateY(10px)}}
|
||
@keyframes pop{from{opacity:0;transform:translateY(8px)}}
|
||
|
||
/* ============ 响应式 ============ */
|
||
@media (max-width:1120px){
|
||
.toc{display:none}
|
||
.tech-grid{display:block}
|
||
}
|
||
@media (max-width:820px){
|
||
.wall{grid-template-columns:repeat(2,1fr)}
|
||
.repos{grid-template-columns:1fr}
|
||
}
|
||
@media (max-width:760px){
|
||
.tb{position:static}
|
||
body{padding-top:0}
|
||
.fsdock{top:12px}
|
||
}
|
||
@media (max-width:560px){
|
||
.sheet{padding:22px}
|
||
.hero{margin:32px 0 30px}
|
||
.pn{flex-direction:column;gap:14px}
|
||
.pn-item.right{align-items:flex-start;text-align:left}
|
||
.tl-item{margin-left:40px}
|
||
.tl-date{left:-46px;width:34px}
|
||
#tout{font-size:13px}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- 内联渐变定义(全部占位图共用,零外部资源) -->
|
||
<svg aria-hidden="true" focusable="false" style="position:absolute;width:0;height:0">
|
||
<defs>
|
||
<linearGradient id="lgA" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#5b7cfa"/><stop offset="1" stop-color="#8b5cf6"/></linearGradient>
|
||
<linearGradient id="lg1" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f7b267"/><stop offset="1" stop-color="#9a3d0f"/></linearGradient>
|
||
<linearGradient id="lg2" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#67e0cf"/><stop offset="1" stop-color="#0d5c56"/></linearGradient>
|
||
<linearGradient id="lg3" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#c4b5fd"/><stop offset="1" stop-color="#4c1d95"/></linearGradient>
|
||
<linearGradient id="lg4" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fda4af"/><stop offset="1" stop-color="#831843"/></linearGradient>
|
||
<linearGradient id="lg5" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#9fb3c8"/><stop offset="1" stop-color="#2f3e52"/></linearGradient>
|
||
<linearGradient id="lg6" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#86efac"/><stop offset="1" stop-color="#14532d"/></linearGradient>
|
||
<linearGradient id="lg7" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fcd34d"/><stop offset="1" stop-color="#92400e"/></linearGradient>
|
||
<linearGradient id="lg8" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#93c5fd"/><stop offset="1" stop-color="#1e3a8a"/></linearGradient>
|
||
<linearGradient id="lg9" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#d4d4d4"/><stop offset="1" stop-color="#404040"/></linearGradient>
|
||
</defs>
|
||
</svg>
|
||
|
||
<!-- 阅读进度条(技术文章页) -->
|
||
<div id="pbar" aria-hidden="true"></div>
|
||
|
||
<!-- 预览工具条:不属于博客 UI -->
|
||
<header class="tb" aria-label="视觉样板预览控制">
|
||
<span class="tb-brand"><b>wait4x</b> · 视觉样板</span>
|
||
<span class="tb-sep"></span>
|
||
<div class="tb-group" role="radiogroup" aria-label="视觉路线">
|
||
<label class="tb-seg"><input type="radio" name="route" value="a"><span>甲·纸墨书卷</span></label>
|
||
<label class="tb-seg"><input type="radio" name="route" value="b"><span>乙·极简灰阶</span></label>
|
||
<label class="tb-seg"><input type="radio" name="route" value="c" checked><span>丙·灰阶骨架×分轨正文<em class="badge">推荐</em></span></label>
|
||
</div>
|
||
<button class="tb-btn" id="schemeBtn" type="button" aria-label="切换明暗模式" title="明暗切换(d)">
|
||
<svg class="ic-moon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><path d="M20.6 13.2A8.2 8.2 0 1 1 10.8 3.4a6.5 6.5 0 0 0 9.8 9.8Z"/></svg>
|
||
<svg class="ic-sun" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2M5 5l1.4 1.4M17.6 17.6 19 19M19 5l-1.4 1.4M6.4 17.6 5 19"/></svg>
|
||
</button>
|
||
<span class="tb-sep"></span>
|
||
<div class="tb-group" role="radiogroup" aria-label="页面类型">
|
||
<label class="tb-seg"><input type="radio" name="page" value="home" id="p-home" checked><span>首页</span></label>
|
||
<label class="tb-seg"><input type="radio" name="page" value="essay" id="p-essay"><span>文章页·书评</span></label>
|
||
<label class="tb-seg"><input type="radio" name="page" value="tech" id="p-tech"><span>文章页·技术</span></label>
|
||
<label class="tb-seg"><input type="radio" name="page" value="gallery" id="p-gallery"><span>图库</span></label>
|
||
<label class="tb-seg"><input type="radio" name="page" value="moments" id="p-moments"><span>瞬间</span></label>
|
||
</div>
|
||
<div class="tb-right">
|
||
<button class="tb-btn" id="keysBtn" type="button" aria-label="快捷键帮助(?)" title="快捷键(?)">
|
||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><rect x="3" y="6.5" width="18" height="11" rx="2"/><path d="M6.5 10h.01M10 10h.01M13.5 10h.01M17 10h.01M9 14h6M6.5 14h.01M17 14h.01"/></svg>
|
||
</button>
|
||
<button class="tb-btn tb-pal" id="paletteBtn" type="button" aria-label="命令面板(Ctrl K 或 /)" title="命令面板(Ctrl K 或 /)">
|
||
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m20.5 20.5-4.6-4.6"/></svg>
|
||
<span>Ctrl K</span>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 站点导航 -->
|
||
<header class="site-head">
|
||
<div class="container site-head-row">
|
||
<a class="site-name" href="#top">wait4x</a>
|
||
<nav class="nav" aria-label="站点导航">
|
||
<label class="nav-item" for="p-home" title="文章">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><rect x="4.5" y="3" width="15" height="18" rx="2.5"/><path d="M9 8h6M9 12h6M9 16h3.5"/></svg>
|
||
<span class="nav-t">文章</span>
|
||
</label>
|
||
<label class="nav-item" for="p-gallery" title="图库">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4.5" width="18" height="15" rx="2.5"/><circle cx="9" cy="10" r="1.7"/><path d="m5 17.5 4.5-4.5 3 3 3.5-3.5 3 3"/></svg>
|
||
<span class="nav-t">图库</span>
|
||
</label>
|
||
<a class="nav-item" href="#about" title="关于">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="8.2" r="3.8"/><path d="M4.5 20c1.6-3.4 4.3-5 7.5-5s5.9 1.6 7.5 5"/></svg>
|
||
<span class="nav-t">关于</span>
|
||
</a>
|
||
<button class="nav-item nav-icon" id="navSearch" type="button" aria-label="搜索(Ctrl K)" title="搜索(Ctrl K)">
|
||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m20.5 20.5-4.6-4.6"/></svg>
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<!-- ============ 首页 ============ -->
|
||
<section class="view view-home">
|
||
<div class="container">
|
||
<div class="hero" style="--i:0">
|
||
<svg class="hero-avatar" viewBox="0 0 60 60" width="60" height="60" role="img" aria-label="头像占位">
|
||
<rect width="60" height="60" rx="14" fill="url(#lgA)"/>
|
||
<circle cx="44" cy="15" r="10" fill="#fff" opacity=".25"/>
|
||
<path d="M0 46 20 28 34 40 46 32 60 46 60 60 0 60Z" fill="#000" opacity=".14"/>
|
||
<text x="11" y="40" font-size="20" font-weight="700" fill="#fff" opacity=".92" font-family="system-ui,sans-serif">w4</text>
|
||
</svg>
|
||
<div>
|
||
<div class="hero-name">wait4x</div>
|
||
<p class="hero-bio">写作者 · 读者 · 快门按压者,拥抱开源。</p>
|
||
<div class="socials">
|
||
<a href="#github" aria-label="GitHub" title="GitHub">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 2C6.48 2 2 6.58 2 12.25c0 4.53 2.87 8.37 6.84 9.73.5.1.68-.22.68-.49 0-.24-.01-.88-.01-1.73-2.78.62-3.37-1.37-3.37-1.37-.45-1.18-1.11-1.5-1.11-1.5-.91-.63.07-.62.07-.62 1 .07 1.53 1.06 1.53 1.06.89 1.57 2.34 1.12 2.91.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.7 0 0 .84-.28 2.75 1.05a9.3 9.3 0 0 1 5 0c1.91-1.33 2.75-1.05 2.75-1.05.55 1.4.2 2.44.1 2.7.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.8-4.57 5.06.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.81 0 .27.18.6.69.49A10.26 10.26 0 0 0 22 12.25C22 6.58 17.52 2 12 2Z"/></svg>
|
||
</a>
|
||
<a href="#rss" aria-label="RSS 订阅" title="RSS">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M4 11.5a8.5 8.5 0 0 1 8.5 8.5M4 4.5a15.5 15.5 0 0 1 15.5 15.5"/><circle cx="5.4" cy="18.6" r="1.5" fill="currentColor" stroke="none"/></svg>
|
||
</a>
|
||
<a href="#mail" aria-label="邮箱" title="邮箱">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5.5" width="18" height="13" rx="2.5"/><path d="m4 7.5 8 5.5 8-5.5"/></svg>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="feed">
|
||
<h2 class="year" style="--i:0">2026</h2>
|
||
|
||
<div class="track" style="--i:1">阅读</div>
|
||
<label class="row" for="p-essay" style="--i:2"><i class="emo" style="--emo:#7c3aed" title="情绪色"></i><span class="row-title">等待戈多</span><span class="row-date">03-31</span></label>
|
||
<label class="row" for="p-essay" style="--i:3"><i class="emo" style="--emo:#0f766e" title="情绪色"></i><span class="row-title">海风中失落的血色馈赠</span><span class="row-date">03-25</span></label>
|
||
<label class="row" for="p-essay" style="--i:4"><i class="emo" style="--emo:#b45309" title="情绪色"></i><span class="row-title">面纱</span><span class="row-date">03-09</span></label>
|
||
<label class="row" for="p-essay" style="--i:5"><i class="emo" style="--emo:#9f1239" title="情绪色"></i><span class="row-title">赎罪</span><span class="row-date">02-26</span></label>
|
||
<label class="row" for="p-essay" style="--i:6"><i class="emo" style="--emo:#4d7c0f" title="情绪色"></i><span class="row-title">泥潭</span><span class="row-date">02-09</span></label>
|
||
|
||
<div class="track" style="--i:7">技术</div>
|
||
<label class="row" for="p-tech" style="--i:8"><span class="row-title">CS各类配置</span><code class="tag">#linux</code><span class="row-date">04-28</span></label>
|
||
</div>
|
||
|
||
<!-- 开源活动:热力图 + 项目卡 -->
|
||
<div class="oss" style="--i:10">
|
||
<div class="track">开源活动</div>
|
||
<div id="heat" role="img" aria-label="近一年开源贡献热力图(示例数据,构建时由 gitea 生成)"></div>
|
||
<div class="heat-meta">
|
||
<span>开源活动 · 数据来自 gitea,构建时生成</span>
|
||
<span class="heat-legend">少<i class="h0"></i><i class="h1"></i><i class="h2"></i><i class="h3"></i><i class="h4"></i>多</span>
|
||
</div>
|
||
<div class="repos">
|
||
<a class="repo" href="https://git.wait4x.online/wait4x/blog" target="_blank" rel="noopener noreferrer">
|
||
<code>wait4x/blog</code>
|
||
<span>本站源码:内容即代码,构建产物零运行时 JS。</span>
|
||
<span class="repo-meta"><i style="--lc:#ff5a03"></i>Astro<b>★ 86</b></span>
|
||
</a>
|
||
<a class="repo" href="https://git.wait4x.online/wait4x/hydro-problems" target="_blank" rel="noopener noreferrer">
|
||
<code>wait4x/hydro-problems</code>
|
||
<span>OJ 题库生成脚本:从题面到判题数据一条流水线。</span>
|
||
<span class="repo-meta"><i style="--lc:#3572a5"></i>Python<b>★ 47</b></span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="latest" style="--i:11">
|
||
<div class="latest-head">
|
||
<span class="lab">最新影像</span>
|
||
<label class="more" for="p-gallery">进入图库 →</label>
|
||
</div>
|
||
<div class="thumbs">
|
||
<figure title="暮色山脊"><svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位"><rect width="80" height="80" fill="url(#lg4)"/><circle cx="56" cy="22" r="9" fill="#fff" opacity=".35"/><path d="M0 80V56l26-18 22 18 16-10 16 20v14Z" fill="#000" opacity=".18"/></svg></figure>
|
||
<figure title="防波堤"><svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位"><rect width="80" height="80" fill="url(#lg2)"/><circle cx="24" cy="30" r="8" fill="#fff" opacity=".3"/><path d="M0 52h80" stroke="#fff" stroke-width="1" opacity=".25"/><path d="M0 80V64l30-8 50 12v12Z" fill="#000" opacity=".18"/></svg></figure>
|
||
<figure title="雪线"><svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位"><rect width="80" height="80" fill="url(#lg8)"/><path d="M0 80 30 34l16 24 12-16 22 38Z" fill="#000" opacity=".2"/><circle cx="62" cy="16" r="6" fill="#fff" opacity=".4"/></svg></figure>
|
||
<figure title="窗台的光"><svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位"><rect width="80" height="80" fill="url(#lg7)"/><rect x="18" y="14" width="44" height="52" rx="3" fill="#fff" opacity=".22"/><rect x="24" y="20" width="32" height="40" rx="2" fill="#fff" opacity=".18"/><path d="M0 80V66l80-10v24Z" fill="#000" opacity=".16"/></svg></figure>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 文章页 · 书评《等待戈多》 ============ -->
|
||
<section class="view view-essay">
|
||
<div class="container">
|
||
<article class="post essay">
|
||
<div class="sheet">
|
||
<header class="post-head">
|
||
<div class="post-cat">Reading</div>
|
||
<h1 class="post-title">等待戈多</h1>
|
||
<div class="post-meta">
|
||
<i class="emo" style="--emo:#7c3aed" title="情绪色"></i>
|
||
<span>2026-03-31</span><span>·</span><span>阅读时长 8 分钟</span>
|
||
</div>
|
||
</header>
|
||
<div class="prose">
|
||
<p>戈多是全书贯穿始终的人物,可并没有任何一个人见过戈多。爱斯特拉冈和弗拉季米尔在乡间小路上等他,等到靴子脱了又穿,等到胡萝卜啃完了又换饼干,等到波卓和幸运儿来了又走,戈多始终没有来。第二天他们接着等,仿佛头一天的一切都可以原封不动地再来一遍。</p>
|
||
<p>「谁是戈多?为何等待?」贝克特对这两个问题一向敬谢不敏,据说他只回过一句:我要是知道,早在戏里说清楚了。于是戈多成了一个空位——有人读出上帝,有人读出希望,有人读出永远缺席的意义本身。空位越空,人们越急着往里填东西;这大概正是这出戏首演七十年后仍然刺人的原因。</p>
|
||
<blockquote class="quote-hero"><p>希望迟迟不来,苦死了等的人。</p></blockquote>
|
||
<p class="quote-src">——萨缪尔·贝克特《等待戈多》</p>
|
||
<p>就戏剧动作而言,《等待戈多》几乎什么都没有发生,而这正是它最残忍的地方:它不安排绝望的事件,只安排绝望的节奏。对话膨胀又塌缩,约定立起又被推翻,戏在「咱们走吧」与「他们不动」之间原地打转。等待本身成了全部的情节,也成了全部的刑罚。</p>
|
||
<blockquote class="pull">
|
||
<p>爱斯特拉冈:咱们走吧。<br>弗拉季米尔:咱们不能。<br>爱斯特拉冈:为什么不能?<br>弗拉季米尔:咱们在等待戈多。</p>
|
||
</blockquote>
|
||
<p>可我并不觉得这是全然的黑暗。两个流浪汉在无望之待里,仍然留住了几样东西:他们拌嘴,也互相系鞋带;他们想上吊,又舍不得用裤腰带;他们每天遇见,每天确认「你又来了」。无望之待没有被拔高成救赎,但陪伴让囚笼有了呼吸的缝隙——自我救赎未必是走出困境,有时只是在困境里替对方把靴子脱下来。</p>
|
||
<p>合上书那天傍晚,我在站台等一班迟迟不来的车,忽然想起幸运儿的思想独白:原来我们都站在各自的乡间小路上,书包里装着胡萝卜,等一个不会来的戈多。贝克特没有给我们答案,只留下一句温柔的嘲讽——他们许诺明天一切都会好起来。信不信,由等的人自己决定。</p>
|
||
</div>
|
||
</div>
|
||
<nav class="pn" aria-label="上下篇">
|
||
<a class="pn-item" href="#prev">
|
||
<span>← 上一篇</span><strong>海风中失落的血色馈赠</strong>
|
||
</a>
|
||
<label class="pn-item right" for="p-tech">
|
||
<span>下一篇 →</span><strong>CS各类配置</strong>
|
||
</label>
|
||
</nav>
|
||
<div class="comments"><b>评论系统占位</b>此处将接入 Artalk(Go 单二进制自托管,sqlite 存储)</div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 文章页 · 技术《CS各类配置》 ============ -->
|
||
<section class="view view-tech">
|
||
<div class="container">
|
||
<div class="tech-grid">
|
||
<article class="post tech">
|
||
<header class="post-head">
|
||
<h1 class="post-title">CS各类配置</h1>
|
||
<div class="post-meta">
|
||
<code class="tag">#docker</code><code class="tag">#debian</code>
|
||
<span>·</span><span>2026-04-28</span>
|
||
</div>
|
||
</header>
|
||
<div class="prose">
|
||
<p>上周把一台 2 核 2G 的入门云服务器重装成 Debian 12,顺手把从裸系统到能跑服务的最小流程整理成清单。目标很简单:换一块干净的地基——初始化用户、换源、装 Docker,全程不超过二十分钟,之后所有服务一律进容器。</p>
|
||
<h2 id="t-sys">系统初始化</h2>
|
||
<p>第一件事是建普通用户、配好密钥,再把密码登录关掉。用 <code>adduser deploy</code> 创建用户并加入 <code>sudo</code> 组,随后在 <code>/etc/ssh/sshd_config</code> 里设置 <code>PasswordAuthentication no</code> 与 <code>PermitRootLogin no</code>,重载 sshd 即可。UFW 只放行 22 与 80/443,云控制台的安全组记得同步。</p>
|
||
<h2 id="t-src">更换软件源</h2>
|
||
<p>官方源对国内机器不算友好,换到清华 TUNA 镜像几乎是必选项。改动集中在 <code>/etc/apt/sources.list</code>,把 <code>deb.debian.org</code> 整体替换即可,bookworm 与 bookworm/updates 两段都要覆盖。</p>
|
||
<figure class="term">
|
||
<div class="term-bar"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span><span class="term-title">bash · deploy@vps</span></div>
|
||
<pre><code><span class="cm"># 换源:备份后替换为清华 TUNA 镜像(Debian 12 / bookworm)</span>
|
||
<span class="pr">$</span> <span class="kw">sudo</span> <span class="cmd">cp</span> /etc/apt/sources.list{,.bak}
|
||
<span class="pr">$</span> <span class="kw">sudo</span> <span class="cmd">sed</span> -i <span class="str">'s|deb.debian.org|mirrors.tuna.tsinghua.edu.cn|g'</span> /etc/apt/sources.list
|
||
<span class="pr">$</span> <span class="kw">sudo</span> <span class="cmd">apt</span> update && <span class="kw">sudo</span> <span class="cmd">apt</span> upgrade -y
|
||
|
||
<span class="cm"># 安装 Docker Engine(官方 APT 仓库)</span>
|
||
<span class="pr">$</span> <span class="cmd">curl</span> -fsSL <span class="str">https://download.docker.com/linux/debian/gpg</span> | <span class="kw">sudo</span> <span class="cmd">gpg</span> --dearmor -o /usr/share/keyrings/docker.gpg
|
||
<span class="pr">$</span> <span class="cmd">echo</span> <span class="str">"deb [arch=amd64 signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable"</span> | <span class="kw">sudo</span> <span class="cmd">tee</span> /etc/apt/sources.list.d/docker.list
|
||
<span class="pr">$</span> <span class="kw">sudo</span> <span class="cmd">apt</span> update && <span class="kw">sudo</span> <span class="cmd">apt</span> install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||
<span class="pr">$</span> <span class="kw">sudo</span> <span class="cmd">systemctl</span> enable --now docker && <span class="kw">sudo</span> <span class="cmd">docker</span> run --rm hello-world</code></pre>
|
||
</figure>
|
||
<h2 id="t-dock">安装 Docker</h2>
|
||
<p>官方的 <code>get.docker.com</code> 脚本虽然省事,但会替你决定源的写入方式;按上面的官方 APT 仓库手动配置更可控,以后升级也只走同一条路。装完用 <code>docker version</code> 确认 Engine 与 compose 插件都在。</p>
|
||
<h3 id="t-mir">国内镜像加速</h3>
|
||
<p>拉取镜像前,在 <code>/etc/docker/daemon.json</code> 里注册镜像加速地址,<code>systemctl restart docker</code> 生效;<code>docker info</code> 的末尾能看到 Registry Mirrors 是否挂载成功。</p>
|
||
</div>
|
||
</article>
|
||
<aside class="toc" aria-label="目录">
|
||
<div class="toc-h">目录</div>
|
||
<a href="#t-sys">系统初始化</a>
|
||
<a href="#t-src">更换软件源</a>
|
||
<a href="#t-dock">安装 Docker</a>
|
||
<a class="toc-sub" href="#t-mir">国内镜像加速</a>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 图库(masonry) ============ -->
|
||
<section class="view view-gallery">
|
||
<div class="container">
|
||
<h1 class="g-title">图库</h1>
|
||
<div class="g-filters" role="group" aria-label="分组筛选(静态示意)">
|
||
<button class="chip on" type="button">全部</button>
|
||
<button class="chip" type="button">2026</button>
|
||
<button class="chip" type="button">街拍</button>
|
||
<button class="chip" type="button">风景</button>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="wall" id="wall">
|
||
<figure class="shot" tabindex="0" style="--ar:3/2">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:晨光街口"><rect width="400" height="300" fill="url(#lg1)"/><circle cx="286" cy="80" r="36" fill="#fff" opacity=".32"/><path d="M0 300V190h44v-42h30v62h36v-92h34v72h42v-46h40v92h58v-60h40v124Z" fill="#000" opacity=".2"/><path d="M0 300v-30l400-40v70Z" fill="#000" opacity=".22"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>晨光街口 · 街拍</strong><span>X-T5 · 23mm f/1.4 · f/2 · 1/500s · ISO 160</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:16/9">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:防波堤"><rect width="400" height="300" fill="url(#lg2)"/><circle cx="120" cy="96" r="30" fill="#fff" opacity=".3"/><path d="M0 168h400" stroke="#fff" stroke-width="2" opacity=".25"/><path d="M0 300V210l120-26 140 30 140-44v130Z" fill="#000" opacity=".18"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>防波堤 · 风景</strong><span>X-T5 · 35mm f/1.4 · f/8 · 1/1000s · ISO 160</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:2/3">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:夜班地铁"><rect width="400" height="300" fill="url(#lg3)"/><circle cx="320" cy="62" r="20" fill="#fff" opacity=".4"/><path d="M0 300V170h50v-40h36v56h44v-96h40v78h48v-52h44v88h56v-64h42v160Z" fill="#000" opacity=".26"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>夜班地铁 · 街拍</strong><span>X-T5 · 35mm f/1.4 · f/1.8 · 1/125s · ISO 3200</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:3/2">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:暮色山脊"><rect width="400" height="300" fill="url(#lg4)"/><circle cx="96" cy="88" r="34" fill="#fff" opacity=".32"/><path d="M0 300 130 130l90 96 70-58 110 132Z" fill="#000" opacity=".2"/><path d="M0 300v-56l400-36v92Z" fill="#000" opacity=".24"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>暮色山脊 · 风景</strong><span>X-T5 · 56mm f/1.2 · f/5.6 · 1/250s · ISO 400</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:1/1">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:雨后屋檐"><rect width="400" height="300" fill="url(#lg5)"/><path d="M40 96 200 40l160 56Z" fill="#000" opacity=".28"/><path d="M70 110v130M120 118v150M170 108v160M220 112v150M270 104v160M320 112v140" stroke="#fff" stroke-width="2" opacity=".22"/><path d="M0 300v-40l400-24v64Z" fill="#000" opacity=".2"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>雨后屋檐 · 街拍</strong><span>X-T5 · 23mm f/2 · f/2.8 · 1/250s · ISO 1250</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:4/5">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:林间小径"><rect width="400" height="300" fill="url(#lg6)"/><path d="M52 0v300M148 0v260M252 0v280M348 0v300" stroke="#000" stroke-width="14" opacity=".18"/><path d="M170 300c30-70 90-120 60-190 70 40 30 130 60 190Z" fill="#fff" opacity=".14"/><path d="M0 300v-30l400-28v58Z" fill="#000" opacity=".2"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>林间小径 · 风景</strong><span>X-T5 · 18-55mm @35mm · f/5.6 · 1/60s · ISO 640</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:3/2">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:窗台的光"><rect width="400" height="300" fill="url(#lg7)"/><rect x="90" y="52" width="220" height="180" rx="6" fill="#fff" opacity=".22"/><rect x="112" y="74" width="176" height="136" rx="4" fill="#fff" opacity=".16"/><path d="M60 300 200 150l20 150Z" fill="#000" opacity=".16"/><path d="M0 300v-26l400-30v56Z" fill="#000" opacity=".18"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>窗台的光 · 街拍</strong><span>X-T5 · 35mm f/1.4 · f/2 · 1/250s · ISO 640</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:16/9">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:雪线"><rect width="400" height="300" fill="url(#lg8)"/><circle cx="316" cy="60" r="24" fill="#fff" opacity=".45"/><path d="M0 300 120 96l70 116 56-72L400 300Z" fill="#000" opacity=".2"/><path d="M120 96l44 72-44 20-40-28Z" fill="#fff" opacity=".28"/><path d="M0 300v-24l400-26v50Z" fill="#000" opacity=".22"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>雪线 · 风景</strong><span>X-T5 · 56mm · f/8 · 1/1500s · ISO 200</span></figcaption>
|
||
</figure>
|
||
<figure class="shot" tabindex="0" style="--ar:4/5">
|
||
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="照片占位:站台"><rect width="400" height="300" fill="url(#lg9)"/><path d="M60 40v180M330 40v180" stroke="#000" stroke-width="10" opacity=".18"/><path d="M40 220h320" stroke="#000" stroke-width="4" opacity=".22"/><path d="M0 300v-44l400-30v74Z" fill="#000" opacity=".22"/><circle cx="200" cy="70" r="26" fill="#fff" opacity=".3"/><text x="12" y="286" font-size="12" fill="#fff" opacity=".55" font-family="system-ui,sans-serif">照片占位</text></svg>
|
||
<figcaption class="exif"><strong>站台 · 街拍</strong><span>X-T5 · 23mm · f/2 · 1/60s · ISO 1600</span></figcaption>
|
||
</figure>
|
||
</div>
|
||
</div>
|
||
<p class="g-note">共 21 张 · EXIF 自动提取 · 格高由照片实际宽高比驱动——真实构建时计算,零 JS</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ 瞬间 · 时间线 ============ -->
|
||
<section class="view view-moments">
|
||
<div class="container">
|
||
<h1 class="g-title">瞬间</h1>
|
||
<p class="g-note">零碎的记录:随手拍、一句话感想、开源动态。</p>
|
||
<ol class="tl">
|
||
<li class="tl-item">
|
||
<time class="tl-date" datetime="2026-10-19">10-19</time>
|
||
<div class="mom-card">
|
||
<figure class="mom-fig"><svg viewBox="0 0 300 200" preserveAspectRatio="xMidYMid slice" role="img" aria-label="随手拍占位:傍晚的云"><rect width="300" height="200" fill="url(#lg7)"/><ellipse cx="90" cy="70" rx="46" ry="14" fill="#fff" opacity=".28"/><ellipse cx="180" cy="50" rx="60" ry="12" fill="#fff" opacity=".22"/><path d="M0 200v-46l60-12 40 8 50-16 60 14 90-10v62Z" fill="#000" opacity=".22"/></svg></figure>
|
||
<p>傍晚的云压得很低,路灯提前亮了。</p>
|
||
<div class="mom-tags"><code class="tag">#随手拍</code><code class="tag">#手机</code></div>
|
||
</div>
|
||
</li>
|
||
<li class="tl-item">
|
||
<time class="tl-date" datetime="2026-10-12">10-12</time>
|
||
<div class="mom-card">
|
||
<p>《人鼠之间》——梦越简单,碎的时候越响。</p>
|
||
<div class="mom-tags"><code class="tag">#阅读</code><code class="tag">#一句话书评</code></div>
|
||
</div>
|
||
</li>
|
||
<li class="tl-item">
|
||
<time class="tl-date" datetime="2026-10-02">10-02</time>
|
||
<div class="mom-card">
|
||
<p>给上游提的 doctor 误报 issue 当天被采纳,第三天随新版本发布——开源最好的正反馈。</p>
|
||
<div class="mom-tags"><code class="tag">#开源</code></div>
|
||
</div>
|
||
</li>
|
||
<li class="tl-item">
|
||
<time class="tl-date" datetime="2026-09-21">09-21</time>
|
||
<div class="mom-card">
|
||
<figure class="mom-fig vt"><svg viewBox="0 0 200 300" preserveAspectRatio="xMidYMid slice" role="img" aria-label="随手拍占位:长椅上的猫"><rect width="200" height="300" fill="url(#lg6)"/><path d="M78 118 92 94l14 24Z" fill="#000" opacity=".55"/><path d="M108 118 122 94l14 24Z" fill="#000" opacity=".55"/><circle cx="100" cy="150" r="42" fill="#000" opacity=".55"/><rect x="30" y="208" width="140" height="14" rx="6" fill="#000" opacity=".3"/><path d="M0 300v-60l200-30v90Z" fill="#000" opacity=".25"/></svg></figure>
|
||
<p>楼下的猫接管了整条长椅。</p>
|
||
<div class="mom-tags"><code class="tag">#随手拍</code><code class="tag">#猫</code></div>
|
||
</div>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<!-- 页脚 -->
|
||
<footer class="site-foot">
|
||
<div class="container">
|
||
<div>© 2026 wait4x · <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">陕ICP备2026014860号-1</a><button class="foot-term" id="footTerm" type="button" aria-label="打开终端(快捷键 ~)" title="终端(~)"><svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="m4 5.5 6.5 6.5L4 18.5"/><path d="M12.5 18.5H20"/></svg></button></div>
|
||
<div class="build">built from <a href="https://git.wait4x.online/wait4x/blog/commit/e3f1a9c" target="_blank" rel="noopener noreferrer">e3f1a9c</a> · 2026-10-24T13:42Z · Astro</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<!-- 字号 dock(书评页) -->
|
||
<div class="fsdock" role="group" aria-label="正文字号">
|
||
<button type="button" data-fs="-2" aria-label="减小字号">A-</button>
|
||
<button type="button" data-fs="r" aria-label="重置字号">A</button>
|
||
<button type="button" data-fs="2" aria-label="增大字号">A+</button>
|
||
</div>
|
||
|
||
<!-- 命令面板 -->
|
||
<dialog id="palette" aria-label="命令面板">
|
||
<input id="pq" type="text" placeholder="搜索文章、页面,或执行动作…" autocomplete="off" spellcheck="false" aria-label="搜索">
|
||
<div id="pr" role="listbox" aria-label="结果列表"></div>
|
||
<div class="pal-hint"><span>↑↓ 选择</span><span>Enter 执行</span><span>Esc 关闭</span></div>
|
||
</dialog>
|
||
|
||
<!-- 快捷键帮助 -->
|
||
<dialog id="keys" aria-label="快捷键帮助">
|
||
<div class="kh">快捷键</div>
|
||
<dl class="klist">
|
||
<dt><kbd>Ctrl</kbd> <kbd>K</kbd> 或 <kbd>/</kbd></dt><dd>命令面板</dd>
|
||
<dt><kbd>1</kbd> <kbd>2</kbd> <kbd>3</kbd></dt><dd>切换路线 甲 / 乙 / 丙</dd>
|
||
<dt><kbd>d</kbd></dt><dd>切换明暗</dd>
|
||
<dt><kbd>g</kbd> <kbd>h</kbd></dt><dd>首页</dd>
|
||
<dt><kbd>g</kbd> <kbd>e</kbd></dt><dd>书评页</dd>
|
||
<dt><kbd>g</kbd> <kbd>t</kbd></dt><dd>技术页</dd>
|
||
<dt><kbd>g</kbd> <kbd>g</kbd></dt><dd>图库</dd>
|
||
<dt><kbd>g</kbd> <kbd>m</kbd></dt><dd>瞬间</dd>
|
||
<dt><kbd>~</kbd></dt><dd>终端</dd>
|
||
<dt><kbd>?</kbd></dt><dd>本帮助</dd>
|
||
</dl>
|
||
<p class="kn">输入框聚焦时快捷键暂停;Esc 关闭浮层。</p>
|
||
</dialog>
|
||
|
||
<!-- 终端彩蛋 -->
|
||
<dialog id="term" aria-label="终端">
|
||
<div class="tbar"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span><span class="ttitle">wait4x@blog — bash</span><button id="tclose" type="button" aria-label="关闭终端"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m5 5 14 14M19 5 5 19"/></svg></button></div>
|
||
<div id="tout" aria-live="polite"></div>
|
||
<form id="tform"><label class="tprompt" for="tin">wait4x@blog:~$</label><input id="tin" autocomplete="off" spellcheck="false" aria-label="终端输入"></form>
|
||
</dialog>
|
||
|
||
<!-- 灯箱 -->
|
||
<dialog id="lb" aria-label="照片查看">
|
||
<button id="lbclose" type="button" class="lbnav" aria-label="关闭"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m5 5 14 14M19 5 5 19"/></svg></button>
|
||
<div id="lbimg"></div>
|
||
<div class="lbbottom">
|
||
<button id="lbprev" type="button" class="lbnav" aria-label="上一张(←)"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m14.5 5.5-6.5 6.5 6.5 6.5"/></svg></button>
|
||
<div id="lbexif"></div>
|
||
<button id="lbnext" type="button" class="lbnav" aria-label="下一张(→)"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9.5 5.5 6.5 6.5-6.5 6.5"/></svg></button>
|
||
</div>
|
||
</dialog>
|
||
|
||
<script>
|
||
const root=document.documentElement,pb=document.getElementById('pbar');
|
||
const $=s=>document.querySelector(s),$$=s=>[...document.querySelectorAll(s)];
|
||
function go(n,v){const r=$(`.tb input[name=${n}][value="${v}"]`);if(r){r.checked=true;r.dispatchEvent(new Event('change'))}}
|
||
for(const i of $$('.tb input'))i.addEventListener('change',()=>{root.dataset[i.name]=i.value;if(i.name==='page')requestAnimationFrame(mason)});
|
||
const flipScheme=()=>{root.dataset.scheme=root.dataset.scheme==='dark'?'light':'dark'};
|
||
$('#schemeBtn').addEventListener('click',flipScheme);
|
||
addEventListener('scroll',()=>{const h=root.scrollHeight-innerHeight;pb.style.width=(h>0?root.scrollTop/h*100:0)+'%'},{passive:true});
|
||
// 命令面板与终端共用数据(样板内写死;真实站点构建时从内容集合生成)
|
||
const POSTS=[
|
||
{t:'等待戈多',c:'阅读 · 书评',k:'贝克特 戏剧 荒诞 等待',go:()=>go('page','essay')},
|
||
{t:'海风中失落的血色馈赠',c:'阅读 · 书评',k:'短篇 小说',go:()=>go('page','essay')},
|
||
{t:'面纱',c:'阅读 · 书评',k:'毛姆 小说',go:()=>go('page','essay')},
|
||
{t:'赎罪',c:'阅读 · 书评',k:'麦克尤恩 小说',go:()=>go('page','essay')},
|
||
{t:'泥潭',c:'阅读 · 书评',k:'小说',go:()=>go('page','essay')},
|
||
{t:'CS各类配置',c:'技术',k:'#docker #debian linux 服务器',go:()=>go('page','tech')},
|
||
];
|
||
const PAGES=[
|
||
{t:'首页 · 文章列表',k:'home blog',go:()=>go('page','home')},
|
||
{t:'文章页 · 书评《等待戈多》',k:'essay reading',go:()=>go('page','essay')},
|
||
{t:'文章页 · 技术《CS各类配置》',k:'tech docker',go:()=>go('page','tech')},
|
||
{t:'图库',k:'gallery photo 照片 影像',go:()=>go('page','gallery')},
|
||
{t:'瞬间 · 时间线',k:'moments timeline 动态',go:()=>go('page','moments')},
|
||
];
|
||
const ACTIONS=[
|
||
{t:'切换明暗模式',k:'theme dark light 明暗',go:flipScheme},
|
||
{t:'路线 甲 · 纸墨书卷',k:'route a 甲',go:()=>go('route','a')},
|
||
{t:'路线 乙 · 极简灰阶',k:'route b 乙',go:()=>go('route','b')},
|
||
{t:'路线 丙 · 灰阶骨架×分轨正文',k:'route c 丙',go:()=>go('route','c')},
|
||
{t:'打开终端',k:'terminal shell 终端',go:openTerm},
|
||
{t:'快捷键帮助',k:'keyboard help 快捷键',go:()=>$('#keys').showModal()},
|
||
];
|
||
const ITEMS=[...POSTS.map(x=>(x.p='文章',x)),...PAGES.map(x=>(x.p='页面',x)),...ACTIONS.map(x=>(x.p='动作',x))];
|
||
// 命令面板:纯前端 include 过滤 + ↑↓/Enter
|
||
const pal=$('#palette'),pq=$('#pq'),pr=$('#pr');let hits=[],ai=0;
|
||
const paint=()=>$$('#pr button').forEach((b,n)=>{b.classList.toggle('on',n===ai);b.setAttribute('aria-selected',n===ai)});
|
||
const render=()=>{const q=pq.value.trim().toLowerCase();hits=ITEMS.filter(x=>!q||(x.t+x.c+x.k+x.p).toLowerCase().includes(q));ai=0;pr.innerHTML=hits.map((h,n)=>`<button type="button" role="option" data-n="${n}" aria-selected="${!n}"><span>${h.t}</span><em>${h.p}</em></button>`).join('')||'<div class="pal-none">没有匹配的结果</div>'};
|
||
const runHit=()=>{const h=hits[ai];if(h){pal.close();h.go()}};
|
||
function openPal(){pq.value='';render();pal.showModal();pq.focus()}
|
||
pq.addEventListener('input',render);
|
||
pq.addEventListener('keydown',e=>{if(e.key==='ArrowDown'){e.preventDefault();ai=Math.min(ai+1,hits.length-1);paint()}else if(e.key==='ArrowUp'){e.preventDefault();ai=Math.max(ai-1,0);paint()}else if(e.key==='Enter'){e.preventDefault();runHit()}});
|
||
pr.addEventListener('click',e=>{const b=e.target.closest('button');if(b){ai=+b.dataset.n;runHit()}});
|
||
pr.addEventListener('mousemove',e=>{const b=e.target.closest('button');if(b&&+b.dataset.n!==ai){ai=+b.dataset.n;paint()}});
|
||
$('#paletteBtn').addEventListener('click',openPal);
|
||
$('#navSearch').addEventListener('click',openPal);
|
||
// 终端彩蛋:黑底绿字琥珀提示符,switch 分发
|
||
const term=$('#term'),tin=$('#tin'),tout=$('#tout');
|
||
const tprint=(s,c)=>{const d=document.createElement('div');if(c)d.className=c;d.textContent=s;tout.appendChild(d);tout.scrollTop=tout.scrollHeight};
|
||
function openTerm(){term.showModal();tin.focus()}
|
||
$('#tform').addEventListener('submit',e=>{e.preventDefault();const raw=tin.value.trim();tin.value='';tprint('wait4x@blog:~$ '+raw,'tc');const a=raw.split(/\s+/),c=a[0];
|
||
switch(c){
|
||
case'':break;
|
||
case'help':['help 列出命令','whoami 站长是谁','ls 列出文章','theme dark|light 切换主题','exit 关闭终端'].forEach(s=>tprint(' '+s));break;
|
||
case'whoami':tprint('wait4x —— 写作者 · 读者 · 快门按压者,拥抱开源。');break;
|
||
case'ls':POSTS.forEach(p=>tprint(` ${p.t}(${p.c})`));break;
|
||
case'theme':a[1]==='dark'||a[1]==='light'?(root.dataset.scheme=a[1],tprint(` theme → ${a[1]}`)):tprint(' 用法:theme dark|light','te');break;
|
||
case'exit':term.close();break;
|
||
default:tprint(` bash: ${c}: command not found`,'te');
|
||
}});
|
||
$('#tclose').addEventListener('click',()=>term.close());
|
||
$('#footTerm').addEventListener('click',openTerm);
|
||
// 贡献热力图:固定种子(样例每次渲染一致,等价构建时生成),周末稀疏 + 三段深色聚集
|
||
let seed=20261024;const rnd=()=>(seed=(seed*1103515245+12345)%2147483648)/2147483648;
|
||
{const BURST=[[7,10],[21,25],[40,44]],end=new Date(Date.UTC(2026,9,24)),f=[];
|
||
for(let w=0;w<53;w++)for(let d=0;d<7;d++){
|
||
const r=rnd();let lv=r<.34?0:r<.6?1:r<.82?2:3;
|
||
if(d>=5)lv=Math.min(lv,1);
|
||
if(BURST.some(([a,b])=>w>=a&&w<=b))lv=Math.min(4,lv+2);
|
||
const dt=new Date(end);dt.setUTCDate(dt.getUTCDate()+(d-5)+(w-52)*7);
|
||
f.push(`<i class="h${lv}" title="${dt.toISOString().slice(0,10)} · ${['无活动','1 次提交','3 次提交','6 次提交','10+ 次提交'][lv]}"></i>`);
|
||
}
|
||
$('#heat').innerHTML=f.join('');}
|
||
// 图库 masonry:span 由占位块渲染高度推算(真实构建时由照片宽高比计算,零 JS)
|
||
const wall=$('#wall');
|
||
function mason(){if(!wall||!wall.offsetHeight)return;for(const s of wall.children)s.style.gridRowEnd=`span ${Math.ceil((s.offsetHeight+14)/22)}`}
|
||
addEventListener('resize',mason);
|
||
// 灯箱:原生 dialog,←→ 切换,Esc 关闭
|
||
const lb=$('#lb'),lbimg=$('#lbimg'),lbexif=$('#lbexif'),shots=$$('.shot');let si=0;
|
||
function showLb(i){si=(i+shots.length)%shots.length;const s=shots[si];const [aw,ah]=s.style.getPropertyValue('--ar').split('/').map(Number);
|
||
const w=Math.min(innerWidth*.92,1100,innerHeight*.72*aw/ah);
|
||
lbimg.innerHTML=s.querySelector('svg').outerHTML;lbimg.firstElementChild.style.width=w+'px';
|
||
lbexif.innerHTML=s.querySelector('figcaption').innerHTML;lb.open||lb.showModal()}
|
||
shots.forEach((s,i)=>{s.addEventListener('click',()=>showLb(i));s.addEventListener('keydown',e=>{if(e.key==='Enter')showLb(i)})});
|
||
lb.addEventListener('keydown',e=>{if(e.key==='ArrowLeft'){e.preventDefault();showLb(si-1)}if(e.key==='ArrowRight'){e.preventDefault();showLb(si+1)}});
|
||
$('#lbprev').addEventListener('click',()=>showLb(si-1));
|
||
$('#lbnext').addEventListener('click',()=>showLb(si+1));
|
||
$('#lbclose').addEventListener('click',()=>lb.close());
|
||
// 书评页字号 dock:--prose-size 三档 18 / 20 / 22
|
||
let pfs=20;const setFs=v=>{pfs=Math.max(18,Math.min(22,v));$('.essay .prose').style.setProperty('--prose-size',pfs+'px')};
|
||
$$('.fsdock button').forEach(b=>b.addEventListener('click',()=>setFs(b.dataset.fs==='r'?20:pfs+ +b.dataset.fs)));
|
||
// 技术页 TOC 当前节高亮
|
||
const tocLinks=$$('.toc a'),tocMap=Object.fromEntries(tocLinks.map(a=>[a.hash.slice(1),a]));
|
||
const io=new IntersectionObserver(es=>{for(const e of es)if(e.isIntersecting){tocLinks.forEach(a=>a.classList.remove('on'));tocMap[e.target.id]?.classList.add('on')}},{rootMargin:'-15% 0px -70% 0px'});
|
||
$$('.tech .prose h2,.tech .prose h3').forEach(h=>io.observe(h));
|
||
$('#keysBtn').addEventListener('click',()=>$('#keys').showModal());
|
||
// 键盘:Ctrl+K / / 面板,1/2/3 路线,d 明暗,g 序列键(300ms 超时),? 帮助,~ 终端;输入聚焦时禁用
|
||
let gk='',gt;
|
||
addEventListener('keydown',e=>{
|
||
if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='k'){e.preventDefault();pal.open?pal.close():openPal();return}
|
||
if(e.ctrlKey||e.metaKey||e.altKey)return;
|
||
const t=e.target;
|
||
if(document.querySelector('dialog[open]')||t.isContentEditable||/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName))return;
|
||
if(e.key==='/'){e.preventDefault();openPal();return}
|
||
if(e.key==='?'){$('#keys').showModal();return}
|
||
if(e.key==='`'||e.key==='~'){openTerm();return}
|
||
if(gk==='g'){clearTimeout(gt);gk='';const m={h:'home',e:'essay',t:'tech',g:'gallery',m:'moments'}[e.key];if(m)go('page',m);return}
|
||
if(e.key==='d')return flipScheme();
|
||
if(e.key==='1')return go('route','a');
|
||
if(e.key==='2')return go('route','b');
|
||
if(e.key==='3')return go('route','c');
|
||
if(e.key==='g'){gk='g';gt=setTimeout(()=>gk='',300)}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|