mcpField
C
cursor-animated-sites

cursor-animated-sites

github.com/rheinmir设计与 UI10240 次安装2 周前更新
代码仓库

关于 cursor-animated-sites

用于制作自包含 HTML 交互演示的技能,通过动画光标逐步浏览文件夹树,讲解生命周期、顺序或数据流。它基于 `/docs-site-macos`,提供手动步进、自动播放、说明文字和按角色着色的节点。

主要功能

  1. 01单文件 HTML 演示
  2. 02动画光标与文件夹树
  3. 03手动步进和自动播放模式
  4. 04按说明文字节奏显示内容
  5. 05按角色着色节点并标注顺序
  6. 06支持减少动态效果偏好

适用场景

  1. 01讲解从安装到运行的生命周期
  2. 02展示操作读取或写入哪些文件
  3. 03逐步呈现一个处理过程
  4. 04制作保留完整最终画面的演示

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rheinmir/setup --skill cursor-animated-sites

安装后在 Claude Code 中输入/cursor-animated-sites确认已加载。

详细介绍

技能:cursor-animated-sites

构建一个单文件、自包含的 HTML演示页面,逐步展示光标在文件夹树上移动的动画。这是叠加在 /docs-site-macos 之上的一层——复用该技能的玻璃设计系统(背景平面、玻璃层级、滚动条、字体、波纹、输出路径 llmwiki/html/DDMMYY-<slug>.html、自动托管)。此技能新增交互式演示。

同样继承 docs-site-macos 的无障碍与文档头部部分,包括 <meta viewport> + <title>/description、内联 favicon、⏮/⏭/▶ 控件上的 :focus-visible 焦点环,以及全局 prefers-reduced-motion 防护(规则 11 已跳过演示动画;该防护还会让光标和打字机动画静止)。请勿在缺少 viewport meta 的情况下发布页面,否则响应式网格会在移动设备上错位。

内容

目的与背景
  • 目的: 构建一个单文件、自包含的 HTML 演示页面(叠加在 /docs-site-macos 之上),展示边栏光标逐步从控制器移动到文件;创建文件时,文件内容会逐字显示;节点按角色着色,并提供手动步进和自动播放模式,以及逐帧说明。
  • 触发条件(何时使用):
    • 解释一个生命周期(安装 → 配置 → 运行)、一个序列或一条数据流。
    • 任何“X 是如何逐步运作的”场景,只要观看者需要看清每个操作会读取/写入哪个文件以及操作顺序。
    • 触发词:“hoạt họa”、“con trỏ chạy”、“life cycle”、“từng bước”、“transcript”、“minh hoạ cây thư mục”、“/cursor-animated-sites”。
  • 非目标: 不重新定义玻璃设计系统(背景平面、玻璃层级、滚动条、字体、波纹、输出路径、自动托管)——复用 /docs-site-macos;此技能只新增交互式演示。
心智模型

STEPS[] (ph · sp · h · d · flow · chain · role · marks · leak · verdict) + 树行(data-born)→ prep(每步强调色、按 born 控制可见性)→ animate(说明文本记录 = 主时钟、born 行逐字显示、光标按顺序访问 chain、角色颜色 + 序号徽标)→ rest = 完整最终帧。

输入与输出约定
字段必填?含义
输入要解释的生命周期/序列是步骤的真实顺序,以及每步读取/写入哪些文件
输入文件夹树(文件 + 参与者)是每个文件都有一个创建步骤
输出llmwiki/html/DDMMYY-<slug>.html是单文件,基于 docs-site-macos,自动托管
输出草稿输出报告 + 索引 + 日志是OKF YAML frontmatter
规则和能力

来之不易的规则(提炼版——不要重新摸索)。这些是从实际使用中总结出的修正。违反它们会让页面难以理解:

  • RULE-01(必须):光标位于左侧边栏中,绝不能覆盖内容。 它是一个向右的小箭头,位于 x = rowLeft − ~22px,主要沿垂直方向移动。将鼠标指针图标放在行上会遮住文字,看起来杂乱。→ 在边栏中使用 ▶ 样式的箭头。平滑移动: left 和 top 必须使用相同的持续时间和缓动(例如 .5s cubic-bezier(.45,.05,.2,1))——时间不匹配(例如 left 为 .32s、top 为 .5s)会导致对角移动卡顿,因为 x 会先于 y 到位。添加 will-change:left,top。
  • RULE-02(必须):为每个访问过的节点编号(内嵌序号标记)。 暂停时,观看者仍必须能看出顺序 ①②③。仅靠移动光标,在画面冻结时无法辨认。
  • RULE-03(必须):默认静止状态 = 当前步骤的完整最终画面,而不是空白的首帧。 加载时显示完整画面(所有节点均已命中,说明文字完整)。绝不要显示什么都没有的空白“第 0 帧”。
  • RULE-04(必须):文件创建之前并不存在。 在文件“诞生”步骤之前,它应为 display:none(从树中消失——不是淡化或半透明显示)。它应当恰好在创建它的步骤中出现(逐字输入)。树应随时间逐渐填满。明确保留一个空白的“安装前”步骤,以便确实呈现空状态。
  • RULE-05(必须):呈现方式要像文字记录,而不是复制粘贴式弹出。 新文件行应以裁剪式打字机效果显示;说明文字应逐字呈现。文字记录是主时钟——画面应以文字记录的速度推进,而不是按固定计时器推进。
  • RULE-06(必须):放慢速度。 阅读速度比动画效果更重要。约每字符 30ms,画面结束时停留较长时间。速度太快会让人放弃观看。
  • RULE-07(必须):按步骤内的角色着色,不要所有内容都用同一种颜色。 最容易造成混淆的是:复制来源和复制目标染成相同颜色 → 无法分辨方向。来源 = 蓝色“读取”,目标 = 青绿色“写入/创建”,传递 = 灰色,拒绝 = 红色,成功 = 绿色,泄漏 = 琥珀色。行上也要添加文字标签。
  • RULE-08(必须):每个步骤的强调色只用于左侧列表和说明文字标签(以便区分列表中的步骤)——右侧节点使用角色颜色。两套配色,各司其职;不要混用在同一个元素上。
  • RULE-09(必须):画面下方的说明文字不得重复左侧栏内容。 左侧 = 精简导航(标签 + 标题)。右侧说明文字 = 当前画面的详细描述和流程。完整重复会让人觉得两者都是多余内容。
  • RULE-10(必须):左栏要窄,把空间留给动画。 grid-template-columns: minmax(190px,.5fr) 1.5fr。
  • RULE-11(必须):两种模式:“逐步播放”(⏭ 播放一帧的文字记录后即停止)和“自动播放”(自动推进,速度较慢)。默认 = 暂停/手动。遵循 prefers-reduced-motion(跳过动画,显示完整画面)。
  • RULE-12(必须):说明文字应使用通俗语言,绝不使用行话或过于简略的表达。 每帧的说明(d)都必须写成完整句子,让项目之外的人也能看懂。每个术语首次出现时都要展开说明:“hook” → “运行时在每次操作前执行的步骤”;“R1/R2” → 用实际措辞说明规则(“禁止写入 raw/ 的规则”);“exit 2 / exit 0” → “代码 2 = 阻止 / 代码 0 = 通过”;“断言” → “要求结果必须为 X 的检查”;“layer=repo” → “在提交/PR 时针对已更改文件运行的检查”。说明读取了什么、写入了什么,以及原因。像“PreToolUse → LÕI → R1 → exit 2”这样简短的符号式说明晦涩难懂,观看者会直接跳过——请写成完整句子;简短的 flow 行可以保留简写。(d 较长时,应降低每字符的打字速度,约 18ms,以免播放太拖沓。)
  • 能力:阅读 /docs-site-macos skill;编写一个 HTML 文件以及 draft/index/log;运行本地 HTML 解析检查,并通过 auto-host server 打开页面。
失败边界
  • 页面缺少 viewport meta → 移动端响应式网格会错乱:失败,不可发布。
  • HTML 解析检查报错 → 失败,托管前修复。
  • 暂停时画面无法读懂(没有 seq 徽标、帧 0 为空、说明文字重复左侧栏内容)→ 不符合 RULE-02/03/09,判定为 失败;修复后重新逐帧检查。

HOW

主工作流
步骤类型输入操作输出/退出失败/下一步
W01effectdocs-site-macos 约定生成基础页面glass 基础页面,位于 llmwiki/html/DDMMYY-<slug>.html—
W02effect基础页面将 body 替换为 hero + controls + lifecycle + legend结构—
W03judgment实际文件列表编写带有 data-born + seqslot 的树行和 actor 芯片树—
W04judgment生命周期编写 STEPS[],先添加空的 "before" 步骤,再添加角色、verdict/leak/marks数据—
W05effect参考引擎加入引擎 JS + CSS 增量;默认暂停/静态可运行页面—
W06deterministic页面验证:HTML 解析 + 暂停时点击 ⏭ 遍历所有步骤每一帧都清晰易读无法读懂 → 修复,重复执行 W06

各步骤的详细说明(W01–W06 的权威依据):

  1. 按照 /docs-site-macos 约定生成基础页面(glass CSS、字体、滚动条、背景平面,输出到 llmwiki/html/DDMMYY-<slug>.html,并自动托管)。
  2. 将 body 替换为上述 hero + controls + .lifecycle(left steps / right tree-pane) + legend 结构。
  3. 编写文件夹树:每个文件对应一个 .trow id="f-*" data-born="…",并以 <span class="seqslot"></span> 开头;.controllers 中也要添加 actor 芯片。
  4. 编写 STEPS[]:按真实生命周期排序,明确先添加一个空的 "before" 步骤;为每个节点标注 role(src/dst);在适用时添加 verdict/leak/marks。
  5. 加入引擎 JS + CSS 增量。默认暂停/静态;⏭ 前进一步,▶ 缓慢自动播放。
  6. 验证:python3 -c "import html.parser; html.parser.HTMLParser().feed(open(F).read())";通过自动托管服务器打开页面;点击 ⏭ 遍历每个步骤,并检查暂停时每一帧是否清晰易读。
分支
ID类型条件行为跳过/失败汇合
B01conditional_requiredprefers-reduced-motion: reduce跳过动画,显示完整帧(RULE-11)—W06
B02user_optional观看者按下 ▶ "自动播放"缓慢自动推进;⏭ 播放一帧后停止默认 = 暂停/手动W06
验证与停止

确定性检查:HTML 解析。需要人工检查:点击 ⏭ 遍历每个步骤,并确认暂停时每一帧都清晰易读。每一帧都通过后停止;然后撰写输出报告。

示例
  • 正例:“制作安装 harness 的生命周期动画页面” → STEPS 以空的 "before install" 开始;步骤 1 的光标依次访问 c-install → f-policy → f-cli,f-policy 以青绿色显示“写入/创建”;暂停帧显示 ①②③ 徽标和完整说明文字。
  • **边界/失败:**源行和目标行都染成步骤强调色 → 观看者无法分辨复制方向 → 违反 RULE-07/08;将右侧面板节点改为 ROLE 颜色(源为蓝色“读取”,目标为青绿色“写入/创建”),并添加文字标签。
参考 — 页面结构
hero (title + 1-line how-to-read)
controls: ⏮  ⏭ Bước tiếp  ▶ Tự chạy   <hint>   <phase chip>   <n / N>
.lifecycle (grid: narrow left | wide right)
 ├─ .steps  (LEFT) — compact cards: <span class=sp>label</span><h3>title</h3>   (NO paragraph)
 └─ .tree-pane (RIGHT, sticky glass)
      ├─ chrome dots + title
      ├─ .controllers — actor chips (installer / agent / git / CI), each with a .seqslot
      ├─ .tree — rows with id=f-*, data-born="<step|−1 pre-exist|99 never>", leading .seqslot
      ├─ .frame-cap — fcNum / fcSp / fcH / fcD(streams) / fcFlow   (the per-frame caption)
      ├─ .cursor — gutter arrow (fill=currentColor)
      └─ .verdict + .leak — floating tags on the RIGHT side near the active row
legend: đọc(blue) · ghi(teal) · cho qua(green) · chặn(red) · lọt(amber)
参考 — STEP 数据模型
const STEPS = [
  { ph:'install'|'run',           // phase (left chip text)
    sp:'CÀI · B0',                // short label
    h:'Đặt lõi vào repo',         // title (left + caption)
    d:'... mô tả đầy đủ ...',     // detailed narration → ONLY in right caption, streams
    flow:'installer → policy.yaml', // 1-line data-flow summary
    chain:['c-install','f-policy','f-cli'],  // node ids the cursor visits IN ORDER
    role:{'f-policy':'dst','f-cli':'dst'},   // per-node role: 'src'|'dst' (pass/actor inferred)
    marks:{'f-x':'warn','f-y':'deny'},       // optional: override role on specific nodes
    leak:'f-wiki',                            // optional: node that "leaked" (amber tag)
    verdict:{kind:'deny'|'ok'|'neutral', text:'exit 2 · CHẶN'} },  // optional: end badge
];

每个文件行上的 data-born:文件创建时所在的步骤索引(-1 = 已存在的供应商文件,99 = 从未创建,例如被阻止的写入尝试)。born === cur 的文件在此步骤中逐字显示;born < cur 的文件已存在;其他文件隐藏。

参考 — 核心 JS(引擎 — 请忠实复现)
const PAL=[{c:'#0a84ff',bg:'rgba(10,132,255,.12)'},{c:'#30b0c7',bg:'rgba(48,176,199,.13)'},
  {c:'#5856d6',bg:'rgba(88,86,214,.12)'},{c:'#34c759',bg:'rgba(52,199,89,.14)'},
  {c:'#ff9500',bg:'rgba(255,149,0,.15)'},{c:'#ff2d55',bg:'rgba(255,45,85,.12)'}];      // màu mỗi step
const ROLEINFO={src:{c:'#0a84ff',t:'đọc',cls:'r-src'}, dst:{c:'#30b0c7',t:'ghi/tạo',cls:'r-dst'},
  pass:{c:'#9aa3b2',t:'',cls:'r-pass'}, actor:{c:'#9aa3b2',t:'',cls:'r-pass'},
  neutral:{c:'#0a84ff',t:'',cls:'r-pass'}, deny:{c:'#e0264b',t:'chặn',cls:'deny'},
  ok:{c:'#34c759',t:'cho qua',cls:'ok'}, warn:{c:'#f0902a',t:'lọt',cls:'warn'},
  attempt:{c:'#e0264b',t:'',cls:'deny'}};

let cur=0, auto=false, timers=[];
const clear=()=>{timers.forEach(clearTimeout);timers=[]};
// con trỏ ở GUTTER trái (x = rowLeft − 22), y = giữa dòng
const at=id=>{const el=document.getElementById(id),pr=pane.getBoundingClientRect(),r=el.getBoundingClientRect();
  return {gx:Math.max(2,r.left-pr.left-22), y:r.top-pr.top+r.height/2, el};};
// hiện nếu pre-existing(-1) hoặc đã tạo ở bước TRƯỚC (b<i); born===i sẽ "gõ" ra trong bước này
function applyVis(i){document.querySelectorAll('.trow[data-born]').forEach(e=>{
  const b=+e.dataset.born; e.classList.toggle('notyet', !((b===-1)||(b!==99&&b<i)));});}

function prep(){ const s=STEPS[cur];
  /* left active card, prog, phase, caption (fcNum/fcSp/fcH/fcD=full/fcFlow) */
  const a=PAL[cur%PAL.length], rs=document.documentElement.style;
  rs.setProperty('--ac',a.c); rs.setProperty('--acbg',a.bg);          // accent PER-STEP (trái + chip)
  document.querySelectorAll('.trow,.chip').forEach(e=>e.classList.remove('hit','deny','ok','warn','attempt','born-now','r-src','r-dst','r-pass'));
  document.querySelectorAll('.rtag').forEach(t=>t.remove());
  document.querySelectorAll('.seqslot').forEach(s2=>{s2.textContent='';s2.className='seqslot';});
  applyVis(cur); return s; }

function paint(idx,s){ const id=s.chain[idx], last=idx===s.chain.length-1, vk=s.verdict?.kind||'';
  const el=document.getElementById(id); el.classList.remove('notyet'); const p=at(id);
  let role = id==='f-raw' ? 'attempt'
    : (s.marks&&s.marks[id]) || (last&&vk) || (s.role&&s.role[id]) || (id.indexOf('c-')===0?'actor':'pass');
  const ri=ROLEINFO[role]||ROLEINFO.pass;
  el.classList.add('hit'); if(ri.cls)el.classList.add(ri.cls); if(id==='f-raw')el.classList.add('attempt');
  const slot=el.querySelector('.seqslot'); if(slot){slot.textContent=idx+1; slot.className='seqslot on '+(ri.cls||'');}
  if(ri.t && id.indexOf('c-')!==0){const tag=document.createElement('span');tag.className='rtag '+ri.cls;tag.textContent=ri.t;el.appendChild(tag);}
  cursor.style.left=p.gx+'px'; cursor.style.top=(p.y-9)+'px'; cursor.style.color=ri.c;   // cursor đổi màu theo vai trò
  /* leak tag + verdict tag positioned on the RIGHT side near p.y */ }

function revealBorn(i){document.querySelectorAll('.trow[data-born]').forEach(e=>{if(+e.dataset.born===i)e.classList.remove('notyet');});}
function typeRow(e,dur){e.classList.remove('notyet'); if(dur<=0)return; e.style.setProperty('--td',dur+'ms');
  e.classList.add('typing'); timers.push(setTimeout(()=>e.classList.remove('typing'),dur+40));}
function typeText(el,txt,per){el.textContent='';el.classList.add('streaming');let i=0;
  const tick=()=>{el.textContent=txt.slice(0,i); i++<txt.length?timers.push(setTimeout(tick,per)):el.classList.remove('streaming');};tick();}

function full(){const s=prep(); revealBorn(cur); s.chain.forEach((id,idx)=>paint(idx,s));}   // TĨNH = frame đầy đủ
function animate(){const s=prep();
  if(reduce){revealBorn(cur); s.chain.forEach((id,idx)=>paint(idx,s)); return;}
  const per=30, capDur=Math.max(1600, s.d.length*per);          // transcript = đồng hồ; CHẬM
  typeText(document.getElementById('fcD'), s.d, per);
  const born=[...document.querySelectorAll('.trow[data-born]')].filter(e=>+e.dataset.born===cur);
  let bt=400; const slot=born.length?(capDur*0.55)/born.length:0;
  born.forEach(e=>{timers.push(setTimeout(()=>typeRow(e,Math.min(slot*0.8,700)),bt)); bt+=slot;});
  const n=Math.max(1,s.chain.length);
  s.chain.forEach((id,idx)=>timers.push(setTimeout(()=>paint(idx,s), 300+Math.round(idx*(capDur-300)/n))));
  if(auto) timers.push(setTimeout(()=>step(cur+1), capDur+2200));   // CHỈ tự sang khi auto
}
function step(i){clear(); cur=(i+STEPS.length)%STEPS.length; animate();}   // Tự bước: gõ 1 frame rồi dừng
// prev/next: auto=false; step(±1).  play: auto=!auto; auto?step(cur):full().
// LOAD: requestAnimationFrame(()=>setTimeout(()=>{cur=0;full();},200));   // mặc định TĨNH frame đầy đủ
参考 — 必要的 CSS 增量(除 docs-site-macos 外)
.lifecycle{display:grid;grid-template-columns:minmax(190px,.5fr) 1.5fr;gap:22px;align-items:start}
.steps{max-height:74vh;overflow-y:auto} .step{opacity:.62} .step.active{opacity:1;border-left:3px solid var(--ac)}
.tree-pane{position:sticky;top:18px} .trow{display:flex;align-items:center;white-space:pre;border-radius:8px;padding:5px 8px}
.seqslot{display:inline-flex;align-items:center;justify-content:center;width:17px;height:17px;margin-right:8px;border-radius:50%;font-size:10px;font-weight:800;background:transparent;color:transparent;border:1px solid rgba(30,90,170,.1)}
.seqslot.r-src{background:#0a84ff;color:#fff;border-color:transparent}
.seqslot.r-dst{background:#30b0c7;color:#fff;border-color:transparent}
.seqslot.r-pass{background:#9aa3b2;color:#fff;border-color:transparent}
.trow.hit.r-src{background:rgba(10,132,255,.13);box-shadow:inset 0 0 0 1px #0a84ff}
.trow.hit.r-dst{background:rgba(48,176,199,.17);box-shadow:inset 0 0 0 1px #30b0c7}
.trow.hit.r-pass{background:rgba(140,150,165,.12);box-shadow:inset 0 0 0 1px rgba(120,130,145,.5)}
.rtag{margin-left:9px;font-size:10px;font-weight:700;padding:1px 8px;border-radius:11px}
.rtag.r-src{background:rgba(10,132,255,.14);color:#0a84ff} .rtag.r-dst{background:rgba(48,176,199,.2);color:#1d7d8c}
.trow.notyet{display:none}                              /* CHƯA tạo → ẩn hẳn (không mờ) */
.trow.typing{overflow:hidden;animation:typeline var(--td,500ms) steps(30) both}
@keyframes typeline{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.cursor{position:absolute;z-index:7;will-change:left,top;color:var(--ac);
  transition:left .5s cubic-bezier(.45,.05,.2,1),top .5s cubic-bezier(.45,.05,.2,1),color .3s ease}  /* MƯỢT: left+top CÙNG duration+easing, đừng lệch */
.cursor svg path{fill:currentColor}                     /* arrow color = currentColor (set per role) */
.fc-sp{background:var(--acbg);color:var(--ac)}          /* caption chip = per-step accent */
交付 — 输出报告

将 propose 草稿写入 llmwiki/wiki/sources/draft/DDMMYY-<ten>.md,并追加到 wiki/index.md + wiki/log.md。 使用 YAML frontmatter(符合 OKF v0.1 标准 — 不要使用粗体 **Type:**,否则会导致 OKF 检查失败):

---
type: draft
title: DDMMYY-<ten>
status: proposed
tags: [cursor-animated-sites, output-report]
timestamp: YYYY-MM-DD
---

# DDMMYY-<ten>

## What
<một câu — trang này dựng/quyết định cái gì>

## Files
| File | Action |
|------|--------|
| `llmwiki/html/DDMMYY-<ten>.html` | created |

## Origin
- **Draft:** `wiki/sources/draft/DDMMYY-<ten>.md`
- **Commit:** _(verify-before-commit điền)_

来源

  • 由 /orca-eval 从构建 llmwiki/html/250626-harness-lifecycle.html 的会话中提炼而来。
  • 基础主题:/docs-site-macos(~/.claude/skills/docs-site-macos/SKILL.md)。

评论

支持 Markdown