Files
blog/posts/f0a1a1f4/index.html
T
biss f839e19947 更新包
-- [CST 2026-08-04 Tuesday 16:05:05]
2026-08-04 16:05:05 +08:00

879 lines
62 KiB
HTML
Raw 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 name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="这篇文章讨论了在生活中追求意义的重要性,特别是在面对死亡时。作者作为一名临终关怀医生,见证了无数病人对未实现目标的遗憾。文章指出,许多人因感到精力不足或时间不够而未能追求自己的目标。然而,研究表明,拥有强烈目标感的人实际上更有活力,更容易被激励。目的不在于做出巨大贡献,而在于找到能激发内心火花的小目标,如阅读、散步或重拾儿时的爱好。文章强调,勇气是追求目标的关键,而不仅仅是勇敢。最后,作者警告说,如果不在有生之年追求目标,可能会在死亡时后悔莫及。"><meta name="author" content="biss"><meta name="keywords" content="学习, 英语"><link rel="canonical" href="https://blog.biss.click/posts/f0a1a1f4/"><link rel="alternate" type="application/rss+xml" title="Bi's Blog RSS" href="https://blog.biss.click/rss.xml"><meta property="og:site_name" content="Bi's Blog"><meta property="og:locale" content="zh_CN"><meta property="og:type" content="article"><meta property="og:title" content="英语文章分享-去奋力生活吧,就好像死亡近在咫尺 | Bi's Blog"><meta property="og:description" content="这篇文章讨论了在生活中追求意义的重要性,特别是在面对死亡时。作者作为一名临终关怀医生,见证了无数病人对未实现目标的遗憾。文章指出,许多人因感到精力不足或时间不够而未能追求自己的目标。然而,研究表明,拥有强烈目标感的人实际上更有活力,更容易被激励。目的不在于做出巨大贡献,而在于找到能激发内心火花的小目标,如阅读、散步或重拾儿时的爱好。文章强调,勇气是追求目标的关键,而不仅仅是勇敢。最后,作者警告说,如果不在有生之年追求目标,可能会在死亡时后悔莫及。"><meta property="og:url" content="https://blog.biss.click/posts/f0a1a1f4/"><meta property="og:image" content="https://pic.biss.click/image/7d55a9c5-3756-4e3b-ace4-92142a2260cd.webp"><meta property="og:image:alt" content="英语文章分享-去奋力生活吧,就好像死亡近在咫尺"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="英语文章分享-去奋力生活吧,就好像死亡近在咫尺 | Bi's Blog"><meta name="twitter:description" content="这篇文章讨论了在生活中追求意义的重要性,特别是在面对死亡时。作者作为一名临终关怀医生,见证了无数病人对未实现目标的遗憾。文章指出,许多人因感到精力不足或时间不够而未能追求自己的目标。然而,研究表明,拥有强烈目标感的人实际上更有活力,更容易被激励。目的不在于做出巨大贡献,而在于找到能激发内心火花的小目标,如阅读、散步或重拾儿时的爱好。文章强调,勇气是追求目标的关键,而不仅仅是勇敢。最后,作者警告说,如果不在有生之年追求目标,可能会在死亡时后悔莫及。"><meta name="twitter:image" content="https://pic.biss.click/image/7d55a9c5-3756-4e3b-ace4-92142a2260cd.webp"><meta property="article:published_time" content="2025-08-11T20:59:46.000Z"><meta property="article:modified_time" content="2025-08-11T20:59:46.000Z"><meta property="article:author" content="biss"><meta property="article:tag" content="学习"><meta property="article:tag" content="英语"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BlogPosting","headline":"英语文章分享-去奋力生活吧,就好像死亡近在咫尺","description":"这篇文章讨论了在生活中追求意义的重要性,特别是在面对死亡时。作者作为一名临终关怀医生,见证了无数病人对未实现目标的遗憾。文章指出,许多人因感到精力不足或时间不够而未能追求自己的目标。然而,研究表明,拥有强烈目标感的人实际上更有活力,更容易被激励。目的不在于做出巨大贡献,而在于找到能激发内心火花的小目标,如阅读、散步或重拾儿时的爱好。文章强调,勇气是追求目标的关键,而不仅仅是勇敢。最后,作者警告说,如果不在有生之年追求目标,可能会在死亡时后悔莫及。","image":["https://pic.biss.click/image/7d55a9c5-3756-4e3b-ace4-92142a2260cd.webp"],"datePublished":"2025-08-11T20:59:46.000Z","dateModified":"2025-08-11T20:59:46.000Z","wordCount":3377,"inLanguage":"zh-CN","mainEntityOfPage":{"@type":"WebPage","@id":"https://blog.biss.click/posts/f0a1a1f4/"},"author":{"@type":"Person","name":"biss","url":"https://github.com/bishshi"},"publisher":{"@type":"Person","name":"biss","image":"https://free.picui.cn/free/2025/08/10/689845496a283.png"},"keywords":"学习, 英语","articleSection":"学习"}</script><link rel="icon" href="/images/Bi.ico"><link rel="manifest" href="/manifest.webmanifest"><link rel="apple-touch-icon" href="/images/apple-touch-icon.png"><meta name="theme-color" content="#f7f5ef" media="(prefers-color-scheme: light)"><meta name="theme-color" content="#0b1014" media="(prefers-color-scheme: dark)"><script defer src="https://umami.biss.click/script.js" data-website-id="3d97b310-b241-4fc6-bc9c-68e317fc7d42"></script><script>window.SITE_CONFIG = {"site":{"url":"https://blog.biss.click","name":"Bi's Blog","description":"Personal blog by biss.","language":"zh-CN","domain":"blog.biss.click","slogan":"好奇心转化为代码的空间。","startTime":"2025-01-01T00:00:00+08:00","copyrightStartYear":2025,"license":"内容采用 CC BY-NC-SA 4.0 协议"},"author":{"name":"biss","email":"bishsh2006@gmail.com","github":"https://github.com/bishshi","avatar":"https://free.picui.cn/free/2025/08/10/689845496a283.png"},"comments":{"envId":"https://comment.biss.click","region":"","script":"https://cdn.jsdelivr.net/npm/twikoo@1.7.7/dist/twikoo.min.js"},"analytics":{"umami":{"script":"https://umami.biss.click/script.js","websiteId":"3d97b310-b241-4fc6-bc9c-68e317fc7d42","statsEndpoint":"https://blogumami.biss.click"}},"talks":{"apiUrl":"https://mm.biss.click/api/echo/page","pageSize":30,"homeLimit":6,"cacheKey":"talksCache","cacheTimeKey":"talksCacheTime","cacheDurationMs":1800000,"fallbackAvatar":"https://free.picui.cn/free/2025/08/10/689845496a283.png"},"search":{"apiKey":"2uxQzk7eD2GBCljdSmIiuU3ause7UK9n","server":{"host":"typesense.biss.click","port":"443","protocol":"https"},"indexName":"blogs","searchParams":{"query_by":"title,content","highlight_full_fields":"title,content","per_page":8,"num_typos":1,"typo_tokens_threshold":1,"prefix":true},"ui":{"maxRetries":10,"retryDelay":100,"animationDuration":300,"maxInitRetries":30,"placeholder":"输入关键词寻找故事..."}},"externalLinks":{"waitSeconds":10,"whitelist":["blog.biss.click","biss.click","github.com","localhost","127.0.0.1"]}};</script><script>
(() => {
const storageKey = 'site-theme';
const media = window.matchMedia('(prefers-color-scheme: dark)');
const getSystemTheme = () => (media.matches ? 'dark' : 'light');
const getSavedTheme = () => {
try {
const theme = localStorage.getItem(storageKey);
return theme === 'dark' || theme === 'light' ? theme : null;
} catch {
return null;
}
};
document.documentElement.dataset.theme = getSavedTheme() || getSystemTheme();
})();
</script><link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/instantsearch.css/themes/reset-min.css"><link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css"><link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free@6.7.2/css/all.min.css"><title>英语文章分享-去奋力生活吧,就好像死亡近在咫尺 | Bi&#39;s Blog</title><link rel="stylesheet" href="/_astro/BaseLayout.DvPRLM9I.css"></head> <body> <header class="site-header"> <nav class="nav"> <a class="brand" href="/" aria-label="Bi's Blog 首页"><span>biss</span><i>.log</i></a> <button class="nav-menu-toggle" type="button" aria-label="打开菜单" aria-expanded="false" data-nav-toggle> <i class="fa-solid fa-bars nav-menu-open" aria-hidden="true"></i> <i class="fa-solid fa-xmark nav-menu-close" aria-hidden="true"></i> </button> <div class="nav-drawer-backdrop" data-nav-backdrop></div> <div class="nav-menu" data-nav-menu> <div class="nav-links nav-center"> <a href="/archives/"><i class="fa-solid fa-box-archive nav-icon" aria-hidden="true"></i><span>归档</span></a><a href="/categories/"><i class="fa-solid fa-folder-open nav-icon" aria-hidden="true"></i><span>分类</span></a><a href="/tags/"><i class="fa-solid fa-tags nav-icon" aria-hidden="true"></i><span>标签</span></a><a href="/shuoshuo/"><i class="fa-regular fa-comments nav-icon" aria-hidden="true"></i><span>说说</span></a><a href="/about/"><i class="fa-solid fa-heart nav-icon" aria-hidden="true"></i><span>关于</span></a><a href="/link/"><i class="fa-solid fa-link nav-icon" aria-hidden="true"></i><span>友链</span></a> </div> <div class="nav-links nav-actions"> <button class="nav-random" type="button" onclick="randomPost()" aria-label="前往随机文章" title="前往随机文章"> <i class="fa-solid fa-shuffle nav-icon" aria-hidden="true"></i> <span>随机文章</span> </button> <button class="nav-search search-typesense-trigger" type="button" aria-label="搜索" title="搜索"> <i class="fa-solid fa-magnifying-glass nav-icon" aria-hidden="true"></i> <span>搜索</span> </button> <button class="nav-console" type="button" aria-label="打开中控台" title="中控台" data-console-open> <i class="fa-solid fa-gauge-high nav-icon" aria-hidden="true"></i> <span>中控台</span> </button> <button class="theme-toggle" type="button" aria-label="切换夜间模式" title="切换夜间模式" data-theme-toggle> <i class="fa-solid fa-sun theme-toggle-sun nav-icon" aria-hidden="true"></i> <i class="fa-solid fa-moon theme-toggle-moon nav-icon" aria-hidden="true"></i> </button> </div> </div> </nav> </header> <div class="control-console" data-console-modal aria-hidden="true"> <div class="control-console-backdrop" data-console-close></div> <section class="control-console-panel" role="dialog" aria-modal="true" aria-labelledby="control-console-title" tabindex="-1"> <header class="control-console-head"> <div> <p>CONTROL CENTER</p> <h2 id="control-console-title">中控台</h2> </div> <button class="control-console-close" type="button" aria-label="关闭中控台" title="关闭" data-console-close> <i class="fa-solid fa-xmark" aria-hidden="true"></i> </button> </header> <div class="control-console-grid"> <a class="control-site-card" href="https://biss.click" target="_blank" rel="noreferrer"> <i class="fa-solid fa-house-chimney" aria-hidden="true"></i> <span> <strong>主站入口</strong> <em>个人主页与常用入口集合</em> </span> <small>biss.click</small> </a><a class="control-site-card" href="https://mm.biss.click" target="_blank" rel="noreferrer"> <i class="fa-regular fa-comments" aria-hidden="true"></i> <span> <strong>说说动态</strong> <em>短句、碎片记录和近况更新</em> </span> <small>mm.biss.click</small> </a><a class="control-site-card" href="https://comment.biss.click" target="_blank" rel="noreferrer"> <i class="fa-solid fa-message" aria-hidden="true"></i> <span> <strong>评论系统</strong> <em>Twikoo 评论服务与访客互动</em> </span> <small>comment.biss.click</small> </a><a class="control-site-card" href="https://umami.biss.click" target="_blank" rel="noreferrer"> <i class="fa-solid fa-chart-line" aria-hidden="true"></i> <span> <strong>站点统计</strong> <em>Umami 访问统计与站点数据</em> </span> <small>umami.biss.click</small> </a> </div> <div class="control-console-info" aria-label="站点信息"> <section> <span>当前站点</span> <strong>blog.biss.click</strong> </section> <section> <span>站点作者</span> <strong>biss</strong> </section> <section> <span>运行起点</span> <strong>2025-01-01</strong> </section> </div> </section> </div> <main> <div class="reading-progress" aria-hidden="true"><span data-reading-progress></span></div> <aside class="reading-resume" data-reading-resume hidden aria-live="polite"> <div class="reading-resume-icon" aria-hidden="true"><i class="fa-solid fa-book-open"></i></div> <div> <strong>继续上次阅读?</strong> <p>你上次读到了 <span data-reading-resume-percent>--</span></p> </div> <div class="reading-resume-actions"> <button type="button" data-reading-continue>继续阅读</button> <button type="button" data-reading-restart>从头开始</button> </div> </aside> <section class="post-layout"> <article class="article post-article"> <header class="article-header article-header-cover"> <img class="article-header-cover-img" src="https://pic.biss.click/image/7d55a9c5-3756-4e3b-ace4-92142a2260cd.webp" alt="" loading="eager" decoding="async"> <div class="article-header-content"> <div class="article-date-line"> <time datetime="2025-08-11T20:59:46.000Z">发布于 2025/08/12</time> </div> <h1>英语文章分享-去奋力生活吧,就好像死亡近在咫尺</h1> <div class="article-hero-meta" aria-label="文章信息"> <span>发布于 2025/08/12</span> <a href="/categories/study/">学习</a> <span>总字数:3.4k</span> <span>阅读时长:9 分钟</span> </div> </div> </header> <section class="article-summary-card" aria-label="文章导读"> <div class="article-summary-header"> <span class="article-summary-brand"> <span class="article-summary-icon" aria-hidden="true"></span>
文章导读
</span> <span class="article-summary-model">摘要由 Qwen 生成</span> </div> <p class="article-summary-content"> <span class="article-summary-text">这篇文章讨论了在生活中追求意义的重要性,特别是在面对死亡时。作者作为一名临终关怀医生,见证了无数病人对未实现目标的遗憾。文章指出,许多人因感到精力不足或时间不够而未能追求自己的目标。然而,研究表明,拥有强烈目标感的人实际上更有活力,更容易被激励。目的不在于做出巨大贡献,而在于找到能激发内心火花的小目标,如阅读、散步或重拾儿时的爱好。文章强调,勇气是追求目标的关键,而不仅仅是勇敢。最后,作者警告说,如果不在有生之年追求目标,可能会在死亡时后悔莫及。</span> </p> </section> <aside class="butterfly-note butterfly-note--warning article-outdated-note" aria-label="文章时效性提示" data-freshness-at="2025-08-11T20:59:46.000Z" data-has-new-version="false" hidden> <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i> <div class="butterfly-note__content"> <p>
注意:本文最后维护于 2025/08/12,距今已超过一年,部分内容可能已经过时,请结合最新资料判断。
</p> </div> </aside> <div class="prose" data-toc="[]"> <p>As a hospice doctor, I often have the privilege of being at the bedside of dying patients.Over the years, Ive had countless conversations about lives well lived, and more often, about regrets.Many of my patients express a strikingly similar sentiment:
I really regret that I never had the energy or time to…
The rest of the sentence varies.For one, it might be writing a book.For another, traveling to a far-off land.For someone else, maybe its training for a favorite sport.The specifics change, but the underlying theme is always the same: they missed out on what I call little p purpose — the process-oriented kind of purpose that comes from doing things that light us up from within.
The heartbreaking truth about these deathbed epiphanies is that the dying no longer have the agency to make things right.Theyve run out of time, or strength, or both.What they once could have done, they no longer can.
A large part of my work, and of the books I write, is trying to convince the living to adopt this mindset before its too late.To offer the lens of the dying as a kind of instruction manual for how to live now.
We must live as if death is just a breath away.
And yet, again and again, I hear the same explanations for why people dont prioritize purpose in their daily lives.
I Dont Have Enough Energy
This one comes up often, especially among young people, busy with careers and raising children.They feel completely depleted by the demands of work and family.The idea of mustering up extra energy to pursue something just for them seems laughable.
But research tells us something interesting.Studies on motivation and physical activity have shown that people with a stronger sense of purpose are actually more likely to feel energized.They experience fewer barriers to action and develop more intrinsic motivation to engage with life.
In other words, energy isnt necessarily a finite pie.It can grow, especially when were engaging with activities that matter to us.
And “purpose” doesnt have to mean starting a nonprofit or launching a million-dollar business.Little p purpose is smaller.Simpler.It could be reading a good book, taking a walk by the lake, or tinkering with a hobby you loved as a kid.Its not about the size of the act, its about the spark.
I Dont Have Enough Time
This one may feel especially true.Were busier than ever, no doubt bombarded by the demands of work, family, texts, emails, and social media.Its one of the most common objections I hear.
But data paints a different picture.
The Bureau of Labor Statistics conducts the American Time Use Survey every year, collecting insights from thousands of participants.The most recent findings?Americans spend an average of five hours a day on leisure and sports activities.Thats across all socioeconomic backgrounds.Rich or poor, employed or not — most people have more discretionary time than they realize.
So, if we do have the time, the question becomes: whats really stopping us?
Purpose Demands Courage
In my years of working with the dying, Ive learned something unexpected.The true barrier to purpose isnt energy, money, or time.
Its courage.
Not bravery in the way we typically define it.Ive had this conversation with war heroes and daredevil athletes.Its not about fearlessness.Its not about skill either.Ive cared for world-class artisans and bestselling authors.Its not about knowledge — most of them knew what mattered to them.
What they lacked was the courage to choose those things.(他们缺乏的是选择那些的勇气)
Its easier to focus on building wealth, pursuing a career, or raising kids.Those are important, yes, but theyre also the low-hanging fruit.They come with clear milestones and societal validation.
Turning toward what lights you up is a much scarier proposition.
What if Im not good enough?
What if I fail?
What if it doesnt turn out how I hoped?
What if people laugh at me?
The list of reasons not to pursue your purpose is long.
But theres one reason that matters more than all the rest:
You might regret it on your deathbed if you dont.</p> </div> <section class="post-copyright-card" aria-label="文章版权信息"> <div class="post-copyright-mark" aria-hidden="true"> <span>CC</span> </div> <div class="post-copyright-main"> <h2>英语文章分享-去奋力生活吧,就好像死亡近在咫尺</h2> <a class="post-copyright-link" href="/posts/f0a1a1f4/">/posts/f0a1a1f4/</a> <dl class="post-copyright-meta"> <div> <dt>作者</dt> <dd>biss</dd> </div> <div> <dt>发布于</dt> <dd><time datetime="2025-08-11T20:59:46.000Z">2025/08/12</time></dd> </div> <div> <dt>许可协议</dt> <dd><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noreferrer">CC BY-NC-SA 4.0</a></dd> </div> </dl> </div> <div class="post-copyright-footer"> <div class="post-copyright-tags" aria-label="文章标签"> <a href="/tags/%E8%8B%B1%E8%AF%AD/">英语</a> </div> <div class="post-copyright-actions" aria-label="文章操作"> <button class="post-copyright-action is-primary post-share-button" type="button" data-share-title="英语文章分享-去奋力生活吧,就好像死亡近在咫尺" data-share-url="https://blog.biss.click/posts/f0a1a1f4/" title="分享文章"> <svg aria-hidden="true" viewBox="0 0 24 24"> <path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"></path> <path d="M12 16V4"></path> <path d="m7 9 5-5 5 5"></path> </svg> <span>分享</span> </button> <a class="post-copyright-action" href="/rss.xml" title="订阅 RSS"> <svg aria-hidden="true" viewBox="0 0 24 24"> <path d="M5 5a14 14 0 0 1 14 14"></path> <path d="M5 11a8 8 0 0 1 8 8"></path> <path d="M6 18h.01"></path> </svg> <span>订阅</span> </a> <a class="post-copyright-action" href="/about/" title="打赏作者"> <svg aria-hidden="true" viewBox="0 0 24 24"> <path d="M7 8h10"></path> <path d="M8 8V5h8v3"></path> <path d="M6 8h12l-1.5 11h-9L6 8Z"></path> <path d="M10 12h4"></path> </svg> <span>打赏</span> </a> <a class="post-copyright-icon" href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fblog.biss.click%2Fposts%2Ff0a1a1f4%2F&amp;text=%E8%8B%B1%E8%AF%AD%E6%96%87%E7%AB%A0%E5%88%86%E4%BA%AB-%E5%8E%BB%E5%A5%8B%E5%8A%9B%E7%94%9F%E6%B4%BB%E5%90%A7%EF%BC%8C%E5%B0%B1%E5%A5%BD%E5%83%8F%E6%AD%BB%E4%BA%A1%E8%BF%91%E5%9C%A8%E5%92%AB%E5%B0%BA" target="_blank" rel="noreferrer" title="分享到 X"> <span aria-hidden="true">x</span> </a> <a class="post-copyright-icon" href="https://service.weibo.com/share/share.php?url=https%3A%2F%2Fblog.biss.click%2Fposts%2Ff0a1a1f4%2F&amp;title=%E8%8B%B1%E8%AF%AD%E6%96%87%E7%AB%A0%E5%88%86%E4%BA%AB-%E5%8E%BB%E5%A5%8B%E5%8A%9B%E7%94%9F%E6%B4%BB%E5%90%A7%EF%BC%8C%E5%B0%B1%E5%A5%BD%E5%83%8F%E6%AD%BB%E4%BA%A1%E8%BF%91%E5%9C%A8%E5%92%AB%E5%B0%BA" target="_blank" rel="noreferrer" title="分享到微博"> <span aria-hidden="true"></span> </a> </div> </div> </section> <script>
(() => {
document.querySelectorAll('.post-share-button').forEach((button) => {
button.addEventListener('click', async () => {
const title = button.dataset.shareTitle || document.title;
const url = button.dataset.shareUrl || window.location.href;
const label = button.querySelector('span');
const originalText = label?.textContent || '分享';
try {
if (navigator.share) {
await navigator.share({ title, url });
return;
}
await navigator.clipboard.writeText(url);
if (label) label.textContent = '已复制';
} catch {
if (label) label.textContent = '复制失败';
} finally {
window.setTimeout(() => {
if (label) label.textContent = originalText;
}, 1800);
}
});
});
})();
</script> <section id="post-comment" class="twikoo-comments" aria-label="Comments" data-env-id="https://comment.biss.click" data-region data-path="/posts/f0a1a1f4/" data-script="https://cdn.jsdelivr.net/npm/twikoo@1.7.7/dist/twikoo.min.js"> <div class="twikoo-comments-head"> <span class="twikoo-comments-icon" aria-hidden="true"> <svg viewBox="0 0 24 24"> <path d="M21 12a8 8 0 0 1-8 8H7l-4 3v-5a8 8 0 1 1 18-6Z"></path> </svg> </span> <div> <h2>Comments</h2> <p>英语文章分享-去奋力生活吧,就好像死亡近在咫尺</p> </div> </div> <div id="twikoo-wrap" class="twikoo-wrap"></div> </section> <script>
(() => {
const root = document.getElementById('post-comment');
const wrap = document.getElementById('twikoo-wrap');
if (!root || !wrap || root.dataset.twikooLoaded === 'true') return;
const envId = root.dataset.envId;
const path = root.dataset.path || window.location.pathname;
const region = root.dataset.region || undefined;
if (!envId) return;
root.dataset.twikooLoaded = 'true';
const initTwikoo = () => {
if (!window.twikoo || typeof window.twikoo.init !== 'function') return;
window.twikoo.init({
el: wrap,
envId,
region,
path,
});
};
if (window.twikoo) {
initTwikoo();
return;
}
const script = document.createElement('script');
script.src = root.dataset.script;
script.defer = true;
script.onload = initTwikoo;
script.onerror = () => {
root.dataset.twikooLoaded = 'false';
};
document.head.appendChild(script);
})();
</script> </article> <aside class="article-sidebar" aria-label="文章侧边栏"> <section class="sidebar-card article-author-card"> <img class="profile-avatar" src="https://free.picui.cn/free/2025/08/10/689845496a283.png" alt="biss" loading="lazy"> <div class="profile-name">biss</div> <div class="article-author-meta"> <span>2025/08/12</span> <a href="/categories/study/">学习</a> </div> </section> <section class="sidebar-card article-tags-card"> <h2> <span aria-hidden="true">#</span>
文章标签
<em>1</em> </h2> <div class="article-tag-list" aria-label="文章标签"> <a href="/tags/%E8%8B%B1%E8%AF%AD/">#英语</a> </div> </section> <div class="article-sticky-sidebar"> <section class="sidebar-card same-category-card"> <h2> <span aria-hidden="true"></span>
同类文章
<em>6</em> </h2> <div class="same-category-list"> <a href="/posts/9b2ef00a/"> <strong>OpenGL- Cohen-Sutherland 编码裁剪算法</strong> <time datetime="2026-04-15T19:34:23.000Z">2026/04/16</time> </a><a href="/posts/7207243b/"> <strong>OpenGL-直线的扫描转换</strong> <time datetime="2026-04-11T19:01:02.000Z">2026/04/12</time> </a><a href="/posts/437a5198/"> <strong>OpenGL-基础程序</strong> <time datetime="2026-04-11T18:20:34.000Z">2026/04/12</time> </a><a href="/posts/c4477b0c/"> <strong>配置OpenGL环境</strong> <time datetime="2026-04-02T04:52:16.000Z">2026/04/02</time> </a><a href="/posts/e9a8e898/"> <strong>C# 基本语法</strong> <time datetime="2025-08-16T18:01:10.000Z">2025/08/17</time> </a><a href="/posts/bc56f789/"> <strong>C# 入门</strong> <time datetime="2025-08-15T09:56:17.000Z">2025/08/15</time> </a> </div> </section> <section class="sidebar-card site-info-card" data-site-info-card data-stats-endpoint="https://blogumami.biss.click"> <h2><i class="fa-solid fa-chart-line" aria-hidden="true"></i> 网站信息</h2> <dl class="site-info-list"> <div> <dt>文章数目</dt> <dd>44</dd> </div><div> <dt>本站总字数</dt> <dd>36,128</dd> </div><div> <dt>本站访客数</dt> <dd data-stat-key="visitors">--</dd> </div><div> <dt>本站访问量</dt> <dd data-stat-key="pageviews">--</dd> </div><div> <dt>最后更新时间</dt> <dd>2026/06/20</dd> </div> </dl> </section> <script>
(() => {
const cacheDuration = 10 * 60 * 1000;
const formatter = new Intl.NumberFormat('en-US');
const cards = [...document.querySelectorAll('[data-site-info-card]')];
const endpoint = cards.find((card) => card.dataset.statsEndpoint)?.dataset.statsEndpoint;
if (!endpoint) return;
const cacheKey = `site-info-stats:${endpoint}`;
const updateCards = (stats) => {
const visitors = Number(stats?.visitors);
const pageviews = Number(stats?.pageviews);
cards.forEach((card) => {
const visitorsEl = card.querySelector('[data-stat-key="visitors"]');
const pageviewsEl = card.querySelector('[data-stat-key="pageviews"]');
if (visitorsEl && Number.isFinite(visitors)) visitorsEl.textContent = formatter.format(visitors);
if (pageviewsEl && Number.isFinite(pageviews)) pageviewsEl.textContent = formatter.format(pageviews);
});
};
try {
const cached = JSON.parse(localStorage.getItem(cacheKey) || 'null');
if (cached?.time && Date.now() - cached.time < cacheDuration) {
updateCards(cached.data);
return;
}
} catch {
localStorage.removeItem(cacheKey);
}
fetch(endpoint, { headers: { Accept: 'application/json' } })
.then((response) => {
if (!response.ok) throw new Error(`Stats request failed: ${response.status}`);
return response.json();
})
.then((data) => {
localStorage.setItem(cacheKey, JSON.stringify({ time: Date.now(), data }));
updateCards(data);
})
.catch(() => {
});
})();
</script> </div> </aside> </section> </main> <footer class="site-footer" aria-label="站点页脚"> <div class="footer-inner"> <section class="footer-top" aria-label="站点信息"> <a class="footer-brand" href="/" aria-label="Bi's Blog 首页"> <img src="/images/Bi.ico" alt="" loading="lazy"> <span>Bi&#39;s Blog</span> </a> <p class="footer-slogan">好奇心转化为代码的空间。</p> </section> <section class="footer-contact" aria-label="联系方式"> <p>联系方式</p> <div class="footer-socials"> <a href="mailto:bishsh2006@gmail.com" aria-label="发送邮件" title="发送邮件"> <i class="fa-solid fa-envelope" aria-hidden="true"></i> </a><a href="/rss.xml" aria-label="订阅 RSS" title="订阅 RSS"> <i class="fa-solid fa-rss" aria-hidden="true"></i> </a><a href="https://github.com/bishshi" target="_blank" rel="noreferrer" aria-label="GitHub" title="GitHub"> <i class="fa-brands fa-github" aria-hidden="true"></i> </a> </div> <nav class="footer-contact-links" aria-label="页脚链接"> <a href="/archives/">发展历程</a> <span aria-hidden="true">|</span><a href="/privacy/">隐私政策</a> </nav> </section> <section class="footer-bottom" aria-label="版权信息"> <div> <p>© 2025 - 2026 Bi&#39;s Blog</p> <p>blog.biss.click | 内容采用 CC BY-NC-SA 4.0 协议</p> </div> <div class="footer-meta"> <p>本站已运行:<span id="site-runtime">计算中...</span></p> <p> <a href="https://fastly.com/" target="_blank" rel="noreferrer">Fastly</a> <span aria-hidden="true">|</span><a href="https://astro.build/" target="_blank" rel="noreferrer">ASTRO</a> </p> </div> </section> </div> </footer> <aside class="hitokoto-card is-collapsed" aria-label="一言" data-hitokoto-card data-astro-cid-4cgfuthx> <div class="hitokoto-card__head" data-astro-cid-4cgfuthx> <button class="hitokoto-card__toggle" type="button" aria-label="展开一言" aria-expanded="false" title="一言" data-hitokoto-toggle data-astro-cid-4cgfuthx> <i class="fa-solid fa-quote-left" aria-hidden="true" data-astro-cid-4cgfuthx></i> <span data-astro-cid-4cgfuthx>一言</span> </button> <div class="hitokoto-card__actions" data-astro-cid-4cgfuthx> <button class="hitokoto-card__refresh" type="button" aria-label="换一句" title="换一句" data-hitokoto-refresh data-astro-cid-4cgfuthx> <i class="fa-solid fa-rotate" aria-hidden="true" data-astro-cid-4cgfuthx></i> </button> <button class="hitokoto-card__collapse" type="button" aria-label="收起一言" title="收起" data-hitokoto-collapse data-astro-cid-4cgfuthx> <i class="fa-solid fa-chevron-down" aria-hidden="true" data-astro-cid-4cgfuthx></i> </button> </div> </div> <a class="hitokoto-card__text" href="https://hitokoto.cn/" target="_blank" rel="noreferrer" data-hitokoto-text data-astro-cid-4cgfuthx>
获取中...
</a> <p class="hitokoto-card__from" data-hitokoto-from data-astro-cid-4cgfuthx>来自 Hitokoto</p> </aside> <script type="module">(()=>{const o=document.querySelector("[data-hitokoto-card]"),c=o?.querySelector("[data-hitokoto-toggle]"),s=o?.querySelector("[data-hitokoto-text]"),l=o?.querySelector("[data-hitokoto-from]"),g=o?.querySelector("[data-hitokoto-refresh]"),k=o?.querySelector("[data-hitokoto-collapse]");if(!(o instanceof HTMLElement)||!(c instanceof HTMLButtonElement)||!(s instanceof HTMLAnchorElement)||!(l instanceof HTMLElement))return;const y="https://yiyan.biss.click/api?encode=json",u="hitokoto-card-cache",d="hitokoto-card-expanded",x=360*60*1e3,f={sentence:"生活明朗,万物可爱。",source:"本地缓存",url:"https://hitokoto.cn/"},S=["/","/index.html"].includes(window.location.pathname),w=window.matchMedia("(max-width: 720px)");function E(){try{const t=localStorage.getItem(d);if(t==="true")return!0;if(t==="false")return!1}catch{}return null}function L(t){try{localStorage.setItem(d,String(t))}catch{}}function r(t,{persist:e=!1}={}){o.classList.toggle("is-collapsed",!t),c.setAttribute("aria-expanded",String(t)),c.setAttribute("aria-label",t?"收起一言":"展开一言"),e&&L(t)}function h(){try{const t=localStorage.getItem(u);if(!t)return null;const e=JSON.parse(t);return!e||typeof e.expiresAt!="number"||e.expiresAt<=Date.now()?null:e.data}catch{return null}}function H(t){try{localStorage.setItem(u,JSON.stringify({data:t,expiresAt:Date.now()+x}))}catch{}}function i(t){s.textContent=t.sentence,s.href=t.url||"https://hitokoto.cn/",l.textContent=t.source?`出自 ${t.source}`:"来自 Hitokoto"}async function p({force:t=!1}={}){if(!t){const e=h();if(e){i(e);return}}o.classList.add("is-loading"),s.textContent="获取中...";try{const e=await fetch(y);if(!e.ok)throw new Error(`Hitokoto request failed: ${e.status}`);const n=await e.json(),a=n.hitokoto||n.content||n.sentence||n.text,q=typeof a=="string"&&a.trim()?a.trim():f.sentence,A=typeof n.from=="string"&&n.from.trim()?n.from.trim():"Hitokoto",M=n.uuid?`https://hitokoto.cn/?uuid=${encodeURIComponent(n.uuid)}`:"https://hitokoto.cn/",m={sentence:q,source:A,url:M};H(m),i(m)}catch(e){console.error(e),i(h()||f)}finally{o.classList.remove("is-loading")}}const b=E();r(b??(S&&!w.matches)),c.addEventListener("click",()=>{r(o.classList.contains("is-collapsed"),{persist:!0})}),g?.addEventListener("click",t=>{t.stopPropagation(),r(!0,{persist:!0}),p({force:!0})}),k?.addEventListener("click",t=>{t.stopPropagation(),r(!1,{persist:!0})}),o.addEventListener("click",t=>{o.classList.contains("is-collapsed")&&(t.target instanceof Element&&t.target.closest("a, button")||r(!0,{persist:!0}))}),p()})();</script> <div class="custom-context-menu" data-context-menu role="menu" aria-hidden="true"> <div class="context-menu-nav" aria-label="页面导航"> <button type="button" data-context-action="back" aria-label="后退上一页" title="后退上一页"> <i class="fa-solid fa-arrow-left" aria-hidden="true"></i> </button> <button type="button" data-context-action="forward" aria-label="前进下一页" title="前进下一页"> <i class="fa-solid fa-arrow-right" aria-hidden="true"></i> </button> <button type="button" data-context-action="reload" aria-label="刷新页面" title="刷新页面"> <i class="fa-solid fa-rotate-right" aria-hidden="true"></i> </button> <a href="/" aria-label="返回首页" title="返回首页"> <i class="fa-solid fa-house" aria-hidden="true"></i> </a> </div> <div class="context-menu-group" data-selection-group hidden> <button type="button" data-context-action="copy-selection" data-selection-item> <i class="fa-regular fa-copy" aria-hidden="true"></i> <span>复制选中文字</span> </button> <button type="button" data-context-action="search-selection" data-selection-item> <i class="fa-brands fa-microsoft" aria-hidden="true"></i> <span>用 Bing 搜索</span> </button> <button type="button" data-context-action="quote-selection" data-selection-item data-comments-item> <i class="fa-regular fa-comment-dots" aria-hidden="true"></i> <span>引用到评论区</span> </button> </div> <div class="context-menu-group" data-image-group hidden> <button type="button" data-context-action="copy-image"> <i class="fa-regular fa-image" aria-hidden="true"></i> <span>复制图片</span> </button> <button type="button" data-context-action="open-image"> <i class="fa-solid fa-up-right-from-square" aria-hidden="true"></i> <span>新页面打开图片</span> </button> <button type="button" data-context-action="copy-image-url"> <i class="fa-solid fa-link" aria-hidden="true"></i> <span>复制图片地址</span> </button> </div> <div class="context-menu-group"> <a href="/archives/"> <i class="fa-solid fa-box-archive" aria-hidden="true"></i> <span>时间线</span> </a> <a href="/categories/"> <i class="fa-solid fa-folder" aria-hidden="true"></i> <span>分类</span> </a> <a href="/tags/"> <i class="fa-solid fa-hashtag" aria-hidden="true"></i> <span>标签</span> </a> <button type="button" data-context-action="random-post"> <i class="fa-solid fa-shuffle" aria-hidden="true"></i> <span>&#x968F;&#x673A;&#x6587;&#x7AE0;</span> </button> <button type="button" data-context-action="comments" data-comments-item> <i class="fa-regular fa-comment-dots" aria-hidden="true"></i> <span>跳转评论区</span> </button> <button type="button" data-context-action="print"> <i class="fa-solid fa-print" aria-hidden="true"></i> <span>打印页面</span> </button> <a href="/cc/"> <i class="fa-solid fa-circle-info" aria-hidden="true"></i> <span>网站版权</span> </a> </div> </div> <div class="context-menu-native-hint" data-context-native-hint role="note" aria-label="Ctrl 右键提示"> <kbd>Ctrl</kbd> + &#x53F3;&#x952E;&#xFF1A;&#x6D4F;&#x89C8;&#x5668;&#x539F;&#x751F;&#x83DC;&#x5355;
</div> <script>
(() => {
const menu = document.querySelector('[data-context-menu]');
if (!menu) return;
const nativeHint = document.querySelector('[data-context-native-hint]');
const selectionItems = [...menu.querySelectorAll('[data-selection-item]')];
const selectionGroup = menu.querySelector('[data-selection-group]');
const commentsItems = [...menu.querySelectorAll('[data-comments-item]')];
const imageGroup = menu.querySelector('[data-image-group]');
const focusableSelector = 'a[href], button:not([disabled])';
let lastSelectionText = '';
let lastImage = null;
let nativeHintTimer = 0;
const isNativeContextTarget = (target) =>
target instanceof Element &&
Boolean(target.closest('input, textarea, select, [contenteditable="true"], [contenteditable=""]'));
const getCommentsTarget = () =>
document.querySelector('.twikoo-comments, #twikoo-wrap, #twikoo, .tk-comments-container');
const getSelectionText = () => String(window.getSelection?.() || '').trim();
const getImageTarget = (target) => {
if (!(target instanceof Element)) return null;
const image = target.closest('img');
return image instanceof HTMLImageElement ? image : null;
};
const getImageUrl = () => {
if (!lastImage) return '';
return lastImage.currentSrc || lastImage.src || lastImage.getAttribute('src') || '';
};
const copyText = async (text) => {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return;
}
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.inset = '0 auto auto -9999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
textarea.remove();
};
const setMenuState = () => {
const hasSelection = lastSelectionText.length > 0;
const hasComments = Boolean(getCommentsTarget());
const hasImage = Boolean(getImageUrl());
selectionItems.forEach((item) => {
item.hidden = !hasSelection;
});
if (selectionGroup) selectionGroup.hidden = !hasSelection;
commentsItems.forEach((item) => {
item.hidden = !hasComments || (item.hasAttribute('data-selection-item') && !hasSelection);
});
if (imageGroup) imageGroup.hidden = !hasImage;
};
const closeMenu = () => {
menu.classList.remove('is-open');
menu.setAttribute('aria-hidden', 'true');
};
const openMenu = (x, y) => {
setMenuState();
menu.classList.add('is-open');
menu.setAttribute('aria-hidden', 'false');
const rect = menu.getBoundingClientRect();
const gap = 10;
const left = Math.min(Math.max(gap, x), window.innerWidth - rect.width - gap);
const top = Math.min(Math.max(gap, y), window.innerHeight - rect.height - gap);
menu.style.left = `${left}px`;
menu.style.top = `${top}px`;
};
const showNativeHint = () => {
if (!nativeHint) return;
window.clearTimeout(nativeHintTimer);
nativeHint.classList.add('is-visible');
nativeHintTimer = window.setTimeout(() => {
nativeHint.classList.remove('is-visible');
}, 2600);
};
const jumpToComments = () => {
const target = getCommentsTarget();
target?.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
const quoteToComments = () => {
jumpToComments();
window.setTimeout(() => {
const textarea = document.querySelector('#twikoo textarea, .twikoo-comments textarea, .tk-input textarea');
if (!(textarea instanceof HTMLTextAreaElement) || !lastSelectionText) return;
const quote = lastSelectionText
.split(/\r?\n/)
.map((line) => `> ${line}`)
.join('\n');
const current = textarea.value.trim();
textarea.value = current ? `${current}\n\n${quote}\n` : `${quote}\n`;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.focus();
}, 500);
};
const searchSelection = () => {
if (!lastSelectionText) return;
const url = `https://www.bing.com/search?q=${encodeURIComponent(lastSelectionText)}`;
window.open(url, '_blank', 'noopener,noreferrer');
};
const copyImage = async () => {
const imageUrl = getImageUrl();
if (!imageUrl) return;
try {
if (!navigator.clipboard?.write || typeof ClipboardItem === 'undefined') {
throw new Error('Image clipboard is not available.');
}
const response = await fetch(imageUrl, { mode: 'cors' });
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({ [blob.type || 'image/png']: blob }),
]);
} catch {
await copyText(new URL(imageUrl, window.location.href).toString());
}
};
const openImage = () => {
const imageUrl = getImageUrl();
if (!imageUrl) return;
window.open(new URL(imageUrl, window.location.href).toString(), '_blank', 'noopener,noreferrer');
};
document.addEventListener('contextmenu', (event) => {
if (event.ctrlKey) {
closeMenu();
nativeHint?.classList.remove('is-visible');
return;
}
if (isNativeContextTarget(event.target)) return;
lastSelectionText = getSelectionText();
lastImage = getImageTarget(event.target);
event.preventDefault();
openMenu(event.clientX, event.clientY);
showNativeHint();
});
document.addEventListener('click', (event) => {
const actionTarget = event.target instanceof Element ? event.target.closest('[data-context-action]') : null;
if (!actionTarget || !menu.contains(actionTarget)) {
closeMenu();
return;
}
const action = actionTarget.dataset.contextAction;
closeMenu();
if (action === 'back') history.back();
if (action === 'forward') history.forward();
if (action === 'reload') location.reload();
if (action === 'copy-selection' && lastSelectionText) copyText(lastSelectionText);
if (action === 'search-selection') searchSelection();
if (action === 'quote-selection') quoteToComments();
if (action === 'copy-image') copyImage();
if (action === 'open-image') openImage();
if (action === 'copy-image-url') copyText(new URL(getImageUrl(), window.location.href).toString());
if (action === 'random-post' && typeof window.randomPost === 'function') window.randomPost(actionTarget);
if (action === 'comments') jumpToComments();
if (action === 'print') window.print();
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') closeMenu();
if (event.key === 'Tab' && menu.classList.contains('is-open')) {
const items = [...menu.querySelectorAll(focusableSelector)].filter((item) => !item.closest('[hidden]'));
if (items.length === 0) return;
const first = items[0];
const last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
});
window.addEventListener('scroll', closeMenu, { passive: true });
window.addEventListener('resize', closeMenu);
})();
</script> <aside class="pwa-toast" data-pwa-toast hidden role="status" aria-live="polite"> <div class="pwa-toast-icon" aria-hidden="true"><i class="fa-solid fa-download"></i></div> <div class="pwa-toast-copy"> <strong data-pwa-title>PWA 提示</strong> <p data-pwa-message></p> </div> <div class="pwa-toast-actions"> <button class="pwa-toast-primary" type="button" data-pwa-primary>确定</button> <button class="pwa-toast-close" type="button" data-pwa-close aria-label="关闭提示">稍后</button> </div> </aside> <script src="https://cdn.jsdmirror.com/npm/instantsearch.js@4.56.0"></script> <script src="https://cdn.jsdmirror.com/npm/typesense-instantsearch-adapter@2.7.0/dist/typesense-instantsearch-adapter.min.js"></script> <script src="https://cdn.jsdmirror.com/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js"></script> <script src="/js/random.js"></script> <script src="/js/search/typesense-search.js"></script> <script src="/js/external-links.js"></script> <script src="/js/pwa.js"></script> <script>
(() => {
const nav = document.querySelector('.nav');
const toggle = document.querySelector('[data-nav-toggle]');
const menu = document.querySelector('[data-nav-menu]');
const backdrop = document.querySelector('[data-nav-backdrop]');
if (!nav || !toggle || !menu) return;
function setMenu(open) {
nav.classList.toggle('is-open', open);
toggle.setAttribute('aria-expanded', String(open));
toggle.setAttribute('aria-label', open ? '关闭菜单' : '打开菜单');
menu.setAttribute('aria-hidden', String(!open && window.innerWidth <= 720));
document.body.classList.toggle('has-nav-drawer', open);
}
toggle.addEventListener('click', () => {
setMenu(!nav.classList.contains('is-open'));
});
backdrop?.addEventListener('click', () => setMenu(false));
menu.addEventListener('click', (event) => {
if (event.target instanceof Element && event.target.closest('a')) setMenu(false);
});
document.addEventListener('click', (event) => {
if (!nav.classList.contains('is-open')) return;
if (event.target instanceof Node && nav.contains(event.target)) return;
setMenu(false);
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && nav.classList.contains('is-open')) setMenu(false);
});
window.addEventListener('resize', () => {
if (window.innerWidth > 720) setMenu(false);
else menu.setAttribute('aria-hidden', String(!nav.classList.contains('is-open')));
});
menu.setAttribute('aria-hidden', String(window.innerWidth <= 720));
})();
(() => {
const modal = document.querySelector('[data-console-modal]');
const panel = modal?.querySelector('.control-console-panel');
const openButtons = document.querySelectorAll('[data-console-open]');
const closeButtons = modal?.querySelectorAll('[data-console-close]') || [];
if (!modal || !panel || openButtons.length === 0) return;
function setConsole(open) {
modal.classList.toggle('is-open', open);
modal.setAttribute('aria-hidden', String(!open));
document.body.classList.toggle('has-control-console', open);
if (open) panel.focus();
}
openButtons.forEach((button) => {
button.addEventListener('click', () => setConsole(true));
});
closeButtons.forEach((button) => {
button.addEventListener('click', () => setConsole(false));
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && modal.classList.contains('is-open')) setConsole(false);
});
})();
(() => {
const storageKey = 'site-theme';
const root = document.documentElement;
const button = document.querySelector('[data-theme-toggle]');
const media = window.matchMedia('(prefers-color-scheme: dark)');
const getSystemTheme = () => (media.matches ? 'dark' : 'light');
const getSavedTheme = () => {
try {
const theme = localStorage.getItem(storageKey);
return theme === 'dark' || theme === 'light' ? theme : null;
} catch {
return null;
}
};
const setSavedTheme = (theme) => {
try {
if (theme === getSystemTheme()) localStorage.removeItem(storageKey);
else localStorage.setItem(storageKey, theme);
} catch {
// localStorage can be unavailable in strict privacy modes.
}
};
const getTheme = () => getSavedTheme() || getSystemTheme();
function applyTheme(theme) {
root.dataset.theme = theme;
root.dataset.themeMode = getSavedTheme() ? 'manual' : 'auto';
button?.setAttribute('aria-label', theme === 'dark' ? '切换到日间模式' : '切换到夜间模式');
button?.setAttribute(
'title',
getSavedTheme()
? (theme === 'dark' ? '切换到日间模式' : '切换到夜间模式')
: `跟随系统:${theme === 'dark' ? '夜间' : '日间'}模式`
);
}
applyTheme(getTheme());
button?.addEventListener('click', () => {
const nextTheme = root.dataset.theme === 'dark' ? 'light' : 'dark';
setSavedTheme(nextTheme);
applyTheme(nextTheme);
});
const handleSystemThemeChange = () => {
if (!getSavedTheme()) applyTheme(getSystemTheme());
};
if (typeof media.addEventListener === 'function') {
media.addEventListener('change', handleSystemThemeChange);
} else if (typeof media.addListener === 'function') {
media.addListener(handleSystemThemeChange);
}
})();
(() => {
const runtime = document.getElementById('site-runtime');
if (!runtime) return;
const start = new Date(window.SITE_CONFIG.site.startTime).getTime();
const pad = (value) => String(value).padStart(2, '0');
function updateRuntime() {
const diff = Math.max(0, Date.now() - start);
const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff / 3600000) % 24);
const minutes = Math.floor((diff / 60000) % 60);
const seconds = Math.floor((diff / 1000) % 60);
runtime.textContent = `${days}${pad(hours)}${pad(minutes)}${pad(seconds)} 秒`;
}
updateRuntime();
window.setInterval(updateRuntime, 1000);
})();
</script> </body> </html> <script>(function(){const postSlug = "f0a1a1f4";
(() => {
const article = document.querySelector('.post-article');
const progressBar = document.querySelector('[data-reading-progress]');
const resume = document.querySelector('[data-reading-resume]');
const resumePercent = document.querySelector('[data-reading-resume-percent]');
const continueButton = document.querySelector('[data-reading-continue]');
const restartButton = document.querySelector('[data-reading-restart]');
if (!article || !progressBar || !resume) return;
const storageKey = `reading-progress:${postSlug}`;
let saved = null;
let canPersist = true;
let frame = 0;
try {
saved = JSON.parse(localStorage.getItem(storageKey) || 'null');
} catch {
saved = null;
}
const articleMetrics = () => {
const top = article.getBoundingClientRect().top + window.scrollY;
const distance = Math.max(1, article.offsetHeight - window.innerHeight);
return { top, distance };
};
const getProgress = () => {
const { top, distance } = articleMetrics();
return Math.min(1, Math.max(0, (window.scrollY - top) / distance));
};
const persist = (progress) => {
if (!canPersist) return;
try {
if (progress >= 0.97) localStorage.removeItem(storageKey);
else if (progress >= 0.02) localStorage.setItem(storageKey, JSON.stringify({ progress, savedAt: Date.now() }));
} catch {
// Storage can be unavailable in strict privacy modes.
}
};
const update = () => {
frame = 0;
const progress = getProgress();
progressBar.style.transform = `scaleX(${progress})`;
persist(progress);
};
const requestUpdate = () => {
if (!frame) frame = window.requestAnimationFrame(update);
};
const closeResume = () => {
resume.hidden = true;
canPersist = true;
};
const savedIsFresh = saved && Date.now() - Number(saved.savedAt) < 90 * 86400000;
const savedProgress = Number(saved?.progress);
if (savedIsFresh && savedProgress >= 0.05 && savedProgress < 0.97) {
canPersist = false;
if (resumePercent) resumePercent.textContent = `${Math.round(savedProgress * 100)}%`;
window.setTimeout(() => { resume.hidden = false; }, 350);
}
continueButton?.addEventListener('click', () => {
closeResume();
const { top, distance } = articleMetrics();
window.scrollTo({ top: top + distance * savedProgress, behavior: 'smooth' });
});
restartButton?.addEventListener('click', () => {
try { localStorage.removeItem(storageKey); } catch {}
closeResume();
window.scrollTo({ top: Math.max(0, articleMetrics().top - 90), behavior: 'smooth' });
});
update();
window.addEventListener('scroll', requestUpdate, { passive: true });
window.addEventListener('resize', requestUpdate);
window.addEventListener('pagehide', () => persist(getProgress()));
})();
})();</script> <script>
(() => {
document.querySelectorAll('.article-outdated-note[data-freshness-at]').forEach((note) => {
const freshnessAt = new Date(note.dataset.freshnessAt || '');
const hasNewVersion = note.dataset.hasNewVersion === 'true';
if (Number.isNaN(freshnessAt.valueOf())) return;
const threshold = new Date();
threshold.setFullYear(threshold.getFullYear() - 1);
note.hidden = !hasNewVersion && freshnessAt >= threshold;
});
})();
</script> <script>
(() => {
const prose = document.querySelector('.prose[data-toc]');
if (!prose) return;
let toc = [];
try {
toc = JSON.parse(prose.dataset.toc || '[]');
} catch {
toc = [];
}
prose.querySelectorAll('h1, h2, h3').forEach((heading, index) => {
const item = toc[index];
if (item?.id && !heading.id) heading.id = item.id;
});
const tocTree = document.querySelector('[data-toc-tree]');
if (tocTree && toc.length > 0) {
const links = [...tocTree.querySelectorAll('[data-toc-link]')];
const groups = [...tocTree.querySelectorAll('[data-toc-group]')];
const headings = toc
.map((item) => document.getElementById(item.id))
.filter(Boolean);
const setActiveToc = (id) => {
links.forEach((link) => {
const isActive = link.getAttribute('href') === `#${id}`;
link.classList.toggle('is-active', isActive);
if (isActive) link.setAttribute('aria-current', 'location');
else link.removeAttribute('aria-current');
});
const activeLink = tocTree.querySelector(`[data-toc-link][href="#${CSS.escape(id)}"]`);
const activeGroup = activeLink?.closest('[data-toc-group]');
groups.forEach((group) => group.classList.toggle('is-open', group === activeGroup));
};
const updateToc = () => {
const anchorOffset = 132;
let activeId = headings[0]?.id;
headings.forEach((heading) => {
if (heading.getBoundingClientRect().top <= anchorOffset) activeId = heading.id;
});
if (activeId) setActiveToc(activeId);
};
tocTree.addEventListener('click', (event) => {
const link = event.target.closest?.('[data-toc-link]');
if (!link) return;
const group = link.closest('[data-toc-group]');
groups.forEach((item) => item.classList.toggle('is-open', item === group));
});
updateToc();
window.addEventListener('scroll', updateToc, { passive: true });
window.addEventListener('resize', updateToc);
}
})();
</script> <script>
(() => {
const prose = document.querySelector('.post-article .prose');
if (!prose) return;
const collapsedLineCount = 8;
const copyLabels = {
idle: '复制',
done: '已复制',
error: '复制失败',
};
const languageNames = {
bash: 'Bash',
c: 'C',
cpp: 'C++',
cs: 'C#',
css: 'CSS',
html: 'HTML',
js: 'JavaScript',
json: 'JSON',
jsx: 'JSX',
md: 'Markdown',
powershell: 'PowerShell',
py: 'Python',
shell: 'Shell',
sh: 'Shell',
ts: 'TypeScript',
tsx: 'TSX',
txt: 'Text',
yaml: 'YAML',
yml: 'YAML',
};
function getCodeLanguage(pre, code) {
const rawLanguage =
pre.dataset.language ||
code.dataset.language ||
[...pre.classList, ...code.classList]
.find((className) => className.startsWith('language-'))
?.replace(/^language-/, '') ||
'';
const normalized = rawLanguage.trim().toLowerCase();
return languageNames[normalized] || normalized.toUpperCase() || 'Code';
}
function getCodeText(code, lines) {
if (lines.length > 0) return lines.map((line) => line.textContent || '').join('\n');
return code.textContent || '';
}
prose.querySelectorAll('img').forEach((image) => {
if (image.closest('a, .article-header-cover')) return;
const src = image.currentSrc || image.getAttribute('src');
if (!src) return;
const link = document.createElement('a');
link.className = 'fancybox';
link.href = src;
link.dataset.fancybox = 'article-gallery';
link.dataset.caption = image.getAttribute('alt') || '';
image.parentNode?.insertBefore(link, image);
link.appendChild(image);
});
prose.querySelectorAll('table').forEach((table) => {
if (table.closest('.table-scroll')) return;
const wrapper = document.createElement('div');
wrapper.className = 'table-scroll';
table.parentNode?.insertBefore(wrapper, table);
wrapper.appendChild(table);
});
prose.querySelectorAll('.butterfly-hide-inline').forEach((item) => {
const button = item.querySelector('.butterfly-hide-toggle');
if (!button) return;
button.addEventListener('click', () => {
item.classList.toggle('is-open');
});
});
if (window.Fancybox) {
window.Fancybox.bind('[data-fancybox="article-gallery"]', {
compact: false,
dragToClose: true,
Images: {
zoom: true,
},
});
}
async function copyText(text) {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return;
}
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.top = '-9999px';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
textarea.remove();
}
prose.querySelectorAll('pre').forEach((pre) => {
if (pre.closest('.code-block')) return;
const code = pre.querySelector('code');
if (!code) return;
const wrapper = document.createElement('div');
wrapper.className = 'code-block';
pre.parentNode?.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const header = document.createElement('div');
header.className = 'code-block-header';
const language = document.createElement('span');
language.className = 'code-block-language';
language.textContent = getCodeLanguage(pre, code);
const toolbar = document.createElement('div');
toolbar.className = 'code-block-toolbar';
const copyButton = document.createElement('button');
copyButton.className = 'code-block-copy';
copyButton.type = 'button';
copyButton.textContent = copyLabels.idle;
copyButton.setAttribute('aria-label', '复制代码');
const toggleButton = document.createElement('button');
toggleButton.className = 'code-block-toggle';
toggleButton.type = 'button';
toggleButton.textContent = '展开';
toggleButton.setAttribute('aria-expanded', 'false');
const expandCue = document.createElement('button');
expandCue.className = 'code-block-expand-cue';
expandCue.type = 'button';
expandCue.setAttribute('aria-label', 'Expand code block');
expandCue.setAttribute('aria-expanded', 'false');
expandCue.innerHTML = '<span aria-hidden="true"></span>';
toolbar.append(copyButton, toggleButton);
header.append(language, toolbar);
wrapper.appendChild(header);
wrapper.appendChild(expandCue);
const codeLines = [...code.querySelectorAll(':scope > .line')];
if (codeLines.length > 0) {
wrapper.classList.add('has-line-numbers');
wrapper.style.setProperty('--line-number-width', `${String(codeLines.length).length + 1}ch`);
codeLines.forEach((line, index) => {
line.dataset.line = String(index + 1);
});
}
const lineCount = codeLines.length || (code.textContent || '').split(/\r?\n/).length;
if (lineCount > collapsedLineCount) wrapper.classList.add('is-collapsible');
copyButton.addEventListener('click', async () => {
try {
await copyText(getCodeText(code, codeLines));
copyButton.textContent = copyLabels.done;
window.setTimeout(() => {
copyButton.textContent = copyLabels.idle;
}, 1400);
} catch {
copyButton.textContent = copyLabels.error;
window.setTimeout(() => {
copyButton.textContent = copyLabels.idle;
}, 1600);
}
});
function toggleExpanded() {
const isExpanded = wrapper.classList.toggle('is-expanded');
toggleButton.textContent = isExpanded ? '收起' : '展开';
toggleButton.setAttribute('aria-expanded', String(isExpanded));
expandCue.setAttribute('aria-expanded', String(isExpanded));
}
toggleButton.addEventListener('click', toggleExpanded);
expandCue.addEventListener('click', toggleExpanded);
});
})();
</script>