Interactive Course

How Claude's Secret
Thinking Words Work

深入 970+ 行 HTML、CSS 和 JavaScript,看看 201 个词是怎么变成一个有轮播、搜索、动画和游戏化的交互式页面的。

6 Modules

从数据到动画

Interactive

小测验 & 实时演示

Zero Jargon

全程大白话解释

Scroll down to begin ↓

01

The Hidden Word Collection

这个应用是干嘛的?201 个词是从哪来的?

You've Seen This Before

Claude Code 在思考的时候,会显示一个小转圈加一个随机词。不是无聊的"思考中..."——而是会蹦出各种好玩的词,比如 Simmering(文火慢炖)、Moonwalking(太空步)、或者 Discombobulating(迷惑中)。

claude-code terminal
Simmering ...
🍳 Cooking 烹饪中
🌑 Moonwalking 太空步
🤖 Clauding 克劳德中
Enchanting 施魔法

Where the Words Live

这些词是硬编码在 Claude Code 的源文件里的——一个巨大的 JavaScript 文件叫 cli.js

有人(ππ!)用 Python正则表达式把所有转圈词从压缩代码里挖了出来(最新版 v2.1.79 有 191 个)。还有 8 个过去式的"完成词"(比如 Crunched),是思考结束后显示的。另外还有 2 个已退役的词(SautéingFlambéing),总共 201 个。

1
找到源文件

cli.jsnode_modules/@anthropic-ai/claude-code/ 目录里

2
搜索已知的词

用 Grep 搜 "Thinking" 或 "Cooking",定位到数组的位置

3
用正则提取

把周围所有大写开头的 "-ing" 词全部提取出来

4
做成探索器

分成 10 个类别,加上 emoji、中文翻译和趣味解释,做成可交互的页面

The Final Product

成品是一个单独的 HTML 文件——大约 970 行代码——打包了轮播、搜索、转圈动画、趣味解释弹窗和收集游戏。不需要外部库,不需要构建工具,不需要服务器。打开文件就能玩。

💡
Why One File Matters

单个自包含的 HTML 文件意味着零配置。你可以发邮件分享、放到 GitHub Pages 上、或者离线打开。这个限制反而逼出了精炼高效的代码——每一行都有存在的意义。

Check Your Understanding

如果你想给 Claude Code 本身加一个新的思考词,应该去哪找?

02

Meet the Data

四个对象撑起了整个页面所需的全部信息

Three Actors, Three Jobs

整个词汇库存放在四个 JavaScript 对象里。你可以把它们想象成四个文件柜,每个柜子存着同一批词的不同信息。

🎨

E — Emoji 映射表

给定一个词比如"Cooking",应该配什么 emoji?E 说:🍳

🇨🇳

Z — 中文翻译表

给定"Cooking",中文怎么说?Z 说:烹饪中

📋

D — 类别列表

词怎么分组?D 定义了 10 个类别,包括图标、颜色和精选词汇。

💬

F — 趣味解释

"Ruminating"为什么配牛的emoji?F 给你一句话解答:🐄 像牛反刍一样把问题嚼了又嚼

Inside the Emoji Map

CODE 代码
const E = {
  'Thinking': '🤔',
  'Cooking':  '🍳',
  'Clauding': '🤖',
  // ... 还有 198 条
};
大白话翻译

创建一个叫 E 的永久查找表。

有人问"Thinking 配什么 emoji?"——答案是:🤔

Cooking 呢?🍳

Clauding(Claude 用自己名字造的词!)配机器人脸 🤖

每个词都按这个模式一一对应。

The Category Blueprint

D 里的每个类别是一个对象,有固定的结构:

CODE 代码
{
  icon: '🧠',
  th:   'purple',
  en:   'Serious Thinking',
  zh:   '正经思考',
  f:    ['Thinking', 'Pondering', ...],
  w:    ['Thinking', 'Pondering', ...]
}
大白话翻译

icon:标签栏显示的 emoji(🧠 大脑代表思考类)

th:主题名——决定背景的浅色调

en/zh:类别的英文名和中文名

f:"精选"词——页面上大卡片展示的 8 个词

w:这个类别的全部词(包含精选的)

🔍
Why Separate f and w?

页面只突出展示 8 个"精选"词。其他词藏在"显示全部"按钮后面。这叫渐进式披露——先展示简单的,需要时再展开复杂的。

Watch Them Work Together

当你点击一个词卡时,三个数据对象会联手合作。来看看它们的"群聊":

Component Chat — When You Click "Simmering"
0 / 6

Check Your Understanding

如果你想加一个新类别叫"运动比喻",里面有 Sprinting(冲刺)和 Dribbling(运球)等词。代码里需要加什么?

03

The Carousel Engine

10 张幻灯片如何用一行 CSS 左右滑动

The Filmstrip Trick

想象一条长长的胶片横着摆放,每一帧刚好是屏幕的宽度。一个窗口(视口)一次只显示一帧。要切换画面,只需要把整条胶片往左或往右拉。

这个轮播就是这么工作的。所有 10 张幻灯片在一个 Flexbox 行里并排放着。因为容器隐藏了溢出的部分,所以一次只能看到一张。

CSS
.slides-area {
  flex: 1;
  overflow: hidden;
}
.slides-track {
  display: flex;
  transition: transform .45s;
}
.slide {
  min-width: 100%;
}
大白话翻译

观察窗口占满可用空间,超出边缘的部分全部隐藏。

里面的所有幻灯片排成一横行。移动这一行时,用 0.45 秒的动画平滑过渡。

每张幻灯片的宽度正好和窗口一样——所以一次只显示一张。

The One-Line Slide

切换幻灯片简单得令人惊讶。一行代码完成所有重活:

JS
function go(i) {
  cur = Math.max(0, Math.min(i, D.length-1));
  track.style.transform =
    `translateX(-${cur*100}%)`;
}
大白话翻译

定义一个叫"go"的函数,接收一个幻灯片编号。

把编号限制在 0 到最后一张之间——这样就不会滑出边界。

把整条轨道向左移动(编号 × 100)%。第 0 张 = 0%,第 1 张 = -100%,第 2 张 = -200%,以此类推。

💡
Transform vs. Left

transform: translateX() 而不是改 left 是一个性能技巧。transform 由 GPU(显卡)处理,即使在老设备上动画也很流畅。

Three Ways to Navigate

轮播同时支持按钮点击、键盘操作和触屏滑动。三种方式都调用同一个 go() 函数:

Arrow Buttons

onclick=go(cur-1)onclick=go(cur+1)。简单的点击处理。

Keyboard

监听左右方向键。同样的函数:go(cur-1)go(cur+1)

👈

Touch Swipe

touchstart 记录手指位置,touchend 时比较。如果滑动距离 >50px,就切换幻灯片。

Check Your Understanding

轮播正在显示第 3 张(索引 2)。用户向左滑动。会应用什么 CSS transform 值?

04

The Slot Machine

转圈动画如何制造出"哒哒哒...停!"的满足感

The Slot Machine Feel

点"随机转一个",词就会飞速闪过,然后逐渐慢下来,直到最后一个词"停住"。感觉就像老虎机——代码的工作原理也很类似。

核心技巧:一个递归函数用越来越长的间隔调用自己。开始时很快(60毫秒),后面逐渐变慢(最多310毫秒),产生自然的减速效果。

Inside spin()

JS
function spin() {
  let c = 0;
  const t = 12 + Math.floor(
    Math.random() * 8
  );
  (function tk() {
    const w = A[Math.floor(
      Math.random() * A.length
    )];
    sW.textContent = w;
    c++;
    if (c < t) {
      setTimeout(tk,
        60 + (c/t) * 250
      );
    }
  })();
}
大白话翻译

从零开始计数。

随机选一个总"转数",在 12 到 19 之间。

定义一个内部函数,每次执行一"跳":

从 201 个词里随机选一个。

把它显示在转圈框里。

计数器加 1。

如果还没到目标次数...

安排下一跳。延迟公式:开始时 60ms,逐渐增加到约 310ms。这就产生了减速效果。

The Deceleration Formula

魔法在这个表达式里:60 + (c/t) * 250

1
第一跳 (c=0)

60 + (0/15) × 250 = 60ms — 飞快

2
一半时 (c=7)

60 + (7/15) × 250 = 177ms — 明显变慢了

3
最后一跳 (c=14)

60 + (14/15) × 250 = 293ms — 落定前的戏剧性停顿

💡
Linear Interpolation (lerp)

公式 起始值 + (进度) * 范围 叫做"线性插值"(lerp)。它能在两个值之间平滑过渡。这个模式在动画、游戏和数据可视化领域无处不在。

The Spinning Icon

在词快速闪过时,一个小图标在四个 Unicode 四分之一圆弧字符之间循环切换,制造旋转效果:

◐ ◓ ◑ ◒

一个 setInterval 每 150ms 切换一次。就是四个字符轮流登场——最简单的动画。

Check Your Understanding

你想让转圈更有戏剧感——开始更快,结束更慢。怎么改延迟公式?

05

Search Everything

一个全屏浮层,支持中英文搜索 201 个词

The Search Data Pipeline

在搜索能工作之前,代码会预先构建一个扁平的列表,包含每个词的全部元数据:

JS
const SD = D.flatMap(c =>
  c.w.map(w => ({
    w,
    zh: Z[w] || '',
    em: E[w] || '',
    cat: c.zh
  }))
);
大白话翻译

对 D 里的每一个类别...

...取出该类别里的每一个词...

...给它打包一个"小档案":

词本身、中文翻译、emoji、所属类别。

把所有档案拍平成一个大的可搜索列表。

💡
flatMap = map + flatten

flatMap 是二合一操作。map 把每个类别转成一堆小档案(产生"列表套列表")。flat 再把它们合并成一个扁平列表。当你需要把分组数据"展开"成一个统一集合时,这是最常用的模式。

The Bilingual Filter

过滤函数非常简洁——检查搜索词是否出现在英文名或中文翻译里:

JS
const m = q
  ? SD.filter(d =>
      d.w.toLowerCase().includes(q)
      || d.zh.includes(q)
    )
  : SD;
大白话翻译

如果有搜索关键词...

只保留满足条件的词:

英文名包含关键词(不区分大小写)...

或者中文翻译包含关键词。

如果没有关键词,显示全部。

Highlight Matches

显示结果时,匹配的文字会用动态正则来高亮:

JS
d.w.replace(
  new RegExp(
    '(' + escapedQuery + ')',
    'gi'
  ),
  '<mark>$1</mark>'
)
大白话翻译

在词的文字中,找到所有与搜索词匹配的地方(不区分大小写,全局搜索)...

...把每个匹配包在 <mark> 标签里,浏览器会自动给它加上高亮背景色。

Check Your Understanding

用户在搜索框里输入"炖"。哪些词会被匹配到?

06

The Collection Game

用 Set、进度条和里程碑弹窗把探索变成游戏

Tracking with a Set

怎么知道用户已经看过哪些词了?你需要一种绝不会存重复的数据结构。请出 Set

JS
const seen = new Set();

function markSeen(w) {
  if (seen.has(w)) return;
  seen.add(w);
  cNum.textContent = seen.size;
  cFill.style.width =
    (seen.size/TOTAL*100) + '%';
}
大白话翻译

创建一个空的"来宾名单"(Set)。

当一个词被点击或转到时:

如果这个词已经在名单上了,啥也不做(不重复计数)。

否则,把它加到名单里。

更新屏幕上的计数数字。

拉伸进度条:(已看词数 / 总数)× 100%。

Milestone Toasts

每 10 个词就会有一个小庆祝从屏幕顶部滑下来。里程碑被定义为一个简单的数组,包含阈值和消息:

JS
const MS = [
  [10,  '🌱', '初来乍到'],
  [50,  '⭐', '半百达成'],
  [100, '💯', '破百!'],
  [201, '🏆', '全部集齐!'],
  // ... 还有 10 个里程碑
];

const m = MS.find(
  x => x[0] === n
);
if (m) showToast(m[1], m[2]);
大白话翻译

定义里程碑检查点:10 个词显示 🌱,50 个显示 ⭐,100 个显示 💯,201 个显示 🏆。

每次计数变化时检查:当前数量有没有命中某个里程碑?

如果命中了,弹出一个带 emoji 和消息的通知。

The Toast Animation

弹窗一开始藏在屏幕上方(top: -60px)。触发时,一个 CSS 类让它滑入视线:

CSS + JS
.toast {
  position: fixed;
  top: -60px;
  transition: top .4s;
}
.toast.show {
  top: 1rem;
}

// JavaScript 里:
toast.classList.add('show');
setTimeout(=>
  toast.classList.remove('show')
, 2500);
大白话翻译

弹窗住在屏幕上方,看不见的地方。

它的位置一旦变化就会用 0.4 秒平滑过渡。

加上"show"类时,从上方滑到距顶部 1rem 的位置。

在 JavaScript 里:加上这个类让它出现...

...2.5 秒后移除这个类,它就滑回上方消失了。

💡
CSS Transitions vs. JS Animation

注意 JS 只负责加/移除一个类——它从不计算位置或帧。CSS 处理所有的动画数学。这种"切换类名,让 CSS 来做动画"的模式是构建 UI 动画最干净的方式。

Final Challenge

用户点了"Cooking"三次。进度计数器增加了几次?

🎉

Course Complete!

你现在明白了 690 行 HTML、CSS 和 JavaScript 是如何创建一个有轮播、老虎机、双语搜索和游戏化的交互式词汇探索器的。

Concepts Learned

Data-driven UI、CSS transform、递归动画、Set 去重追踪、class toggle transitions、regex highlighting

Try It Yourself

打开在线探索器 看看实际效果

由 ππ 制作 — 课程由 Claude 生成