Skip to content

我在博客里内置了一个终端,可以 cd、ls、cat 看文章

Published at:

最近心血来潮,想给自己的博客网站加一个有趣的小彩蛋,想法是用一种类似终端命令行的方式去访问博客中的内容。终端本身对开发者而言非常熟悉,但把它搬到网站中应该可以带来意想不到的惊喜感。(如果你还没有体验,建议现在先暂停一下阅读,去主页寻找这个隐藏的小彩蛋。)但是由于本人不擅长前端,尤其是 CSS,因此在与 ChatGPT 还有 Claude 激烈地争论,折腾了整整 3 晚后,终于成功实现了这个功能,成品效果还不错。

设计思路

功能定位

设计这个“终端”彩蛋时,我希望达到以下几个目的:首先,彩蛋的入口是隐蔽的,不会影响正常用户的浏览体验,也让它更神秘一点,作为一个bonus;同时要提供尽量真实的终端体验,支持常见的 shell 命令如 lscdcatpwd 等,最重要的,让用户能够通过命令行的方式浏览文章,提供另一种独特的导航体验,最后还要有流畅的交互动画,让进入和退出终端时都有合适的视觉效果。

核心实现

文件系统模拟

为了让终端体验更加真实,我模拟了一个简单的文件系统结构:

/
├── home/
├── posts/
├── archives/
├── tags/ 
└── me/

通过 VitePress 的主题数据,我可以获取到所有文章的元数据,然后将标题转换为文件名的形式放在 /posts/ 目录下。用户可以通过 ls 命令查看所有文章,用 cat 命令阅读文章内容。

命令解析与执行

实现的常用 Shell 命令包括:

  • 文件导航类
    • pwd:显示当前工作目录
    • ls:列出当前目录的文件/子目录
    • cd:切换工作目录,支持相对路径和绝对路径
  • 内容查看类
    • catheadtail:查看文件内容
  • 辅助工具类
    • help:显示帮助信息
    • clear:清屏
    • exit:关闭终端

比较有趣的设计是,当用户 cd 到其他页面目录时,终端会自动跳转到对应的网页,这样就实现了通过命令行导航博客的功能。(比如在根目录 \ 时,执行 cd archives 这个命令,网页会自动跳转到我的 Archives 目录)。

交互体验优化

为了让终端用户体验更好,我又增加了下列的功能:

  1. Tab 自动补全:支持命令和文件名的 Tab 补全
  2. 命令历史:用上下箭头键可以查看命令历史
  3. 组合键支持Ctrl+C 取消当前输入
  4. 滚动控制:终端打开时禁用背景页面滚动,关闭时恢复
  5. 中文输入法兼容:处理了中文输入法的 composition 事件

视觉设计

为了提升用户体验和视觉的舒适感,终端的视觉设计借鉴了现代终端模拟器的风格。首先,整个终端使用了毛玻璃背景,同时使用渐变阴影,使用多层阴影营造深度感。在进入和退出终端时有舒缓的动画切换页面,整体缓慢舒适,并且在跳转网页的时候也有一定的动画过渡。

最重要的,提示符号我设为了我最喜欢的 符号,它是金色的,并且他会发光!

一些小细节

输入处理

在实现终端的输入处理逻辑时,一个绕不过去的问题就是中文输入法的兼容性。中文输入不像英文字符是即时输入的,而是经历一个“组合输入”(composition)阶段:用户先输入拼音,系统再给出候选词,用户选择后才会将最终文字写入输入框。如果不加处理,这个过程中按下 Enter 很容易提前触发命令执行,导致未完成的输入被误当作有效指令提交。

为了解决这个问题,我监听了 compositionstart 和 compositionend 事件,用 isComposing 标记当前是否处于输入法的组合状态。当用户处于组合输入时,Enter 会被完全忽略。更进一步,为了防止某些浏览器在输入结束瞬间触发 Enter 的“时差执行”,我引入了 lastCompositionEnd 时间戳,并在 onEnter 中判断用户是否在输入结束 极短时间内(大约 20~30ms 内)触发了回车。如果是,那也视为“误触”并跳过处理。

不同浏览器对输入法行为的实现略有差异,所以我还区分了 Chrome、Safari 等环境,为它们设置了不同的时间阈值。这种方式比单纯用 skipNextEnter 这种布尔值要更鲁棒。

一个微妙但重要的点是:Tab 自动补全不应该被视为“组合输入”行为的一部分。所以在 handleTab 中我手动重置 lastCompositionEnd,确保用户在输入中文后使用 Tab 补全时,接下来的 Enter 能被正确识别并立即执行命令。否则会出现用户刚输入完中文,用 Tab 补全了命令,再按 Enter 结果被忽略的奇怪现象。

彩蛋命令

当然,作为一个彩蛋,少不了一些有趣的隐藏命令:

  • 当用户尝试 rm -rf / 时,终端会回复 "You bad guy"
  • sudo 命令会提示 "No no. You are not invited."
  • 尝试删除或移动文件时会提示权限不足

彩蛋在哪里

最后,揭晓一下彩蛋的位置--点击主页的头像!

彩蛋还会持续更新。。。